mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
runtime: add per-game radial stick response
This commit is contained in:
@@ -608,6 +608,14 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
|
||||
rt.deadzone = static_cast<int>(n);
|
||||
rt.has_deadzone = true;
|
||||
}
|
||||
if (ct.contains("anti_deadzone")) {
|
||||
const auto n = toml::find<int64_t>(ct, "anti_deadzone");
|
||||
if (n < 0 || n > 32767)
|
||||
throw std::runtime_error(fmt::format(
|
||||
"[controller] anti_deadzone out of range (0..32767): {}", n));
|
||||
rt.anti_deadzone = static_cast<int>(n);
|
||||
rt.has_anti_deadzone = true;
|
||||
}
|
||||
}
|
||||
|
||||
return rt;
|
||||
|
||||
@@ -447,6 +447,12 @@ struct RuntimeConfig {
|
||||
bool has_deadzone = false;
|
||||
int deadzone = 0;
|
||||
|
||||
// anti_deadzone: minimum radial analog output after leaving deadzone, in
|
||||
// raw SDL axis units (0..32767). This is a game-owned response setting used
|
||||
// to compensate a title's own internal stick deadzone. Absent => 0.
|
||||
bool has_anti_deadzone = false;
|
||||
int anti_deadzone = 0;
|
||||
|
||||
};
|
||||
|
||||
// One entry from [[recompiler.bios_vectors]].
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef PSX_STICK_H
|
||||
#define PSX_STICK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Convert one SDL stick vector to the two unsigned DualShock axis bytes.
|
||||
*
|
||||
* deadzone and anti_deadzone use SDL's 0..32767 radial magnitude scale.
|
||||
* Values inside deadzone are centred. Outside it, magnitude is remapped from
|
||||
* anti_deadzone to full travel while direction is preserved. anti_deadzone=0
|
||||
* is an ordinary rescaled radial deadzone.
|
||||
*/
|
||||
void psx_stick_to_dualshock(int16_t x, int16_t y,
|
||||
int deadzone, int anti_deadzone,
|
||||
uint8_t *out_x, uint8_t *out_y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -111,6 +111,7 @@ endif()
|
||||
|
||||
set(PSXRECOMP_RUNTIME_SOURCES
|
||||
${PSXRECOMP_ROOT}/runtime/src/main.cpp
|
||||
${PSXRECOMP_ROOT}/runtime/src/psx_stick.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/memory.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/gpu.c
|
||||
${PSXRECOMP_ROOT}/runtime/src/ws_ui_group.c
|
||||
|
||||
+7
-26
@@ -66,6 +66,7 @@ extern "C" void psx_event_step_conservative_env_init(void);
|
||||
#include "iso_reader.h" /* text-image guard: extract the boot EXE from the disc */
|
||||
#include "launcher_device.h" /* recomp-ui controller-source round-trip */
|
||||
#include "psx_keybinds.h" /* configurable keyboard->DualShock keybinds (keybinds.ini) */
|
||||
#include "psx_stick.h" /* radial SDL-stick -> DualShock response transform */
|
||||
#if defined(RECOMP_LAUNCHER)
|
||||
#include "recomp_launcher.h" /* shared recomp-ui Dear ImGui launcher */
|
||||
#include "launcher_profile.h" /* per-system variant profile (theme/caps bundle) */
|
||||
@@ -2103,6 +2104,7 @@ struct PsxButtonMap {
|
||||
|
||||
static int controller_device_index = 0;
|
||||
static int controller_deadzone = 12000;
|
||||
static int controller_anti_deadzone = 0;
|
||||
static std::array<PsxButtonMap, 16> controller_map = {{
|
||||
{ PAD_UP, "up", {} },
|
||||
{ PAD_DOWN, "down", {} },
|
||||
@@ -2608,33 +2610,10 @@ static uint16_t controller_pad_buttons(SDL_GameController* h, bool suppress_stic
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/* Radial deadzone: process a stick's X and Y together so the dead region is a
|
||||
* small CIRCLE, not a per-axis square. Travel within controller_deadzone reads
|
||||
* centred (0x80/0x80); beyond it the vector MAGNITUDE is rescaled to the full
|
||||
* range along the stick's true direction, so diagonals are preserved (a
|
||||
* per-axis deadzone snaps diagonals toward the cardinals and feels notchy —
|
||||
* bad for directional analog like Ape Escape's net swing). The magnitude is
|
||||
* capped at 32767 before rescale so a full-diagonal push (raw mag ~46341) maps
|
||||
* to ~0x9E/0x9E per axis — the circular gate a real DualShock stick reports,
|
||||
* not 0xFF/0xFF. At dz==0 it reduces to a plain magnitude-preserving map. */
|
||||
static void axes_to_pad_pair(int16_t vx, int16_t vy, uint8_t* obx, uint8_t* oby) {
|
||||
const double dz = controller_deadzone;
|
||||
double x = vx, y = vy;
|
||||
double mag = std::sqrt(x * x + y * y); /* 0 .. ~46341 */
|
||||
if (mag <= dz || mag <= 0.0) { *obx = 0x80; *oby = 0x80; return; }
|
||||
double capped = mag > 32767.0 ? 32767.0 : mag;
|
||||
double range = 32767.0 - dz;
|
||||
if (range < 1.0) range = 1.0;
|
||||
double newmag = (capped - dz) * 32767.0 / range; /* 0 .. 32767 */
|
||||
double scale = newmag / mag; /* along true direction */
|
||||
int sx = (int)std::lround(x * scale);
|
||||
int sy = (int)std::lround(y * scale);
|
||||
if (sx > 32767) sx = 32767; else if (sx < -32768) sx = -32768;
|
||||
if (sy > 32767) sy = 32767; else if (sy < -32768) sy = -32768;
|
||||
int bx = (sx + 32768) >> 8;
|
||||
int by = (sy + 32768) >> 8;
|
||||
*obx = (uint8_t)(bx < 0 ? 0 : (bx > 255 ? 255 : bx));
|
||||
*oby = (uint8_t)(by < 0 ? 0 : (by > 255 ? 255 : by));
|
||||
psx_stick_to_dualshock(vx, vy,
|
||||
controller_deadzone, controller_anti_deadzone,
|
||||
obx, oby);
|
||||
}
|
||||
|
||||
/* Buttons for a player's selected device (0xFFFF = none pressed). `player` is
|
||||
@@ -5154,6 +5133,8 @@ int main(int argc, char** argv) {
|
||||
ctrl_lock_mode = gc.runtime.controller_lock_mode;
|
||||
ctrl_lock_device = gc.runtime.controller_lock_device;
|
||||
if (gc.runtime.has_deadzone) resolved_deadzone = gc.runtime.deadzone;
|
||||
if (gc.runtime.has_anti_deadzone)
|
||||
controller_anti_deadzone = gc.runtime.anti_deadzone;
|
||||
{ const char *e = std::getenv("PSX_GL_FORCE_CPU_PRESENT");
|
||||
if (e && e[0] && e[0] != '0') g_gl_fbo_present = 0; }
|
||||
game_entry_pc = gc.entry_pc;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "psx_stick.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static int clamp_axis_setting(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return 0;
|
||||
if (value > 32767)
|
||||
return 32767;
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint8_t signed_axis_to_byte(int value)
|
||||
{
|
||||
int byte = (value + 32768) >> 8;
|
||||
if (byte < 0)
|
||||
byte = 0;
|
||||
if (byte > 255)
|
||||
byte = 255;
|
||||
return (uint8_t)byte;
|
||||
}
|
||||
|
||||
void psx_stick_to_dualshock(int16_t x, int16_t y,
|
||||
int deadzone, int anti_deadzone,
|
||||
uint8_t *out_x, uint8_t *out_y)
|
||||
{
|
||||
const double raw_x = (double)x;
|
||||
const double raw_y = (double)y;
|
||||
const double magnitude = sqrt(raw_x * raw_x + raw_y * raw_y);
|
||||
double source_magnitude;
|
||||
double travel;
|
||||
double output_magnitude;
|
||||
double scale;
|
||||
int scaled_x;
|
||||
int scaled_y;
|
||||
|
||||
deadzone = clamp_axis_setting(deadzone);
|
||||
anti_deadzone = clamp_axis_setting(anti_deadzone);
|
||||
|
||||
if (deadzone == 32767 ||
|
||||
magnitude <= (double)deadzone || magnitude <= 0.0) {
|
||||
*out_x = 0x80;
|
||||
*out_y = 0x80;
|
||||
return;
|
||||
}
|
||||
|
||||
source_magnitude = magnitude > 32767.0 ? 32767.0 : magnitude;
|
||||
travel = (source_magnitude - (double)deadzone) /
|
||||
(32767.0 - (double)deadzone);
|
||||
if (travel < 0.0)
|
||||
travel = 0.0;
|
||||
if (travel > 1.0)
|
||||
travel = 1.0;
|
||||
|
||||
output_magnitude = (double)anti_deadzone +
|
||||
travel * (32767.0 - (double)anti_deadzone);
|
||||
scale = output_magnitude / magnitude;
|
||||
scaled_x = (int)lround(raw_x * scale);
|
||||
scaled_y = (int)lround(raw_y * scale);
|
||||
|
||||
if (scaled_x < -32768)
|
||||
scaled_x = -32768;
|
||||
if (scaled_x > 32767)
|
||||
scaled_x = 32767;
|
||||
if (scaled_y < -32768)
|
||||
scaled_y = -32768;
|
||||
if (scaled_y > 32767)
|
||||
scaled_y = 32767;
|
||||
|
||||
*out_x = signed_axis_to_byte(scaled_x);
|
||||
*out_y = signed_axis_to_byte(scaled_y);
|
||||
}
|
||||
Reference in New Issue
Block a user