From aa188048f1b67dcd4a078686248d4dc8a3888403 Mon Sep 17 00:00:00 2001 From: hmk Date: Fri, 10 Apr 2020 12:23:08 -0400 Subject: [PATCH] render: Scale relative mouse motion better for logical sizing From hmk: "When scaling is enabled (e.g. via SDL_RenderSetLogicalSize, size not equal to window size), mouse motion events are also scaled. Small motions are rounded up (SDL_max() when the value after scaling is less than 1), while larger motions are truncated by the floating point -> integer conversion. https://hg.libsdl.org/SDL/file/b18197f9bf9d/src/render/SDL_render.c#l658 The end result feels something like mouse reverse mouse acceleration + angle snapping at low speeds, but less consistent (amount of truncation & rounding depends on how fast the mouse is moved) and potentially much worse if the scaling factor is large. This pretty much makes it useless for anything where you need precise mouse aiming (think of games). I suspect this is why aiming gets so terrible in some games that let you use scaling to reduce the render resolution (e.g. Ion Fury). With 4x4 scaling, I can reproduce a situation where it takes three fast flicks of the mouse across the pad to undo one slow sweep across the pad. In other words, extreme reverse acceleration. This does not happen when scaling is disabled. Furthermore, any game that uses relative mouse motion events for 3D camera rotation probably wants the raw mouse deltas and not a value that depends on scaling and resolution and rounding and truncation. Ideal camera rotation just takes mouse input, multiplies it by sensitivity, and adds it to the angle-in-radians or whatever measure is used for yaw & pitch. Pixels and screen resolution or window dimensions should not be a part of the equation at all, even if it could be implemented without rounding errors. [...] This [patch] completely eliminates angle snapping for me, and makes sensitivity consistent. In other words, it's completely usable for, say, aiming in a first person shooter." Partially fixes Bugzilla #4811. --- src/render/SDL_render.c | 18 ++++++++++-------- src/render/SDL_sysrender.h | 4 ++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index c53452d66..0c4cfe95e 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -660,15 +660,17 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y); event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x)); event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y)); - if (event->motion.xrel > 0) { - event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x))); - } else if (event->motion.xrel < 0) { - event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x))); + if (event->motion.xrel != 0) { + float rel = renderer->xrel + event->motion.xrel / (scale.x * renderer->dpi_scale.x); + float trunc = SDL_truncf(rel); + renderer->xrel = rel - trunc; + event->motion.xrel = trunc; } - if (event->motion.yrel > 0) { - event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y))); - } else if (event->motion.yrel < 0) { - event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y))); + if (event->motion.yrel != 0) { + float rel = renderer->yrel + event->motion.yrel / (scale.y * renderer->dpi_scale.y); + float trunc = SDL_truncf(rel); + renderer->yrel = rel - trunc; + event->motion.yrel = trunc; } } } diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index d6b2f3dab..ee6f7312c 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -189,6 +189,10 @@ struct SDL_Renderer /* The pixel to point coordinate scale */ SDL_FPoint dpi_scale; + /* Remainder from scaled relative motion */ + float xrel; + float yrel; + /* The list of textures */ SDL_Texture *textures; SDL_Texture *target;