Scale N64 analog optimally for different gate shapes (#834)

* Scale N64 analog optimally wrt gate shapes

* Limit max_range to near diagonals

* Make magic numbers adjustable constants
This commit is contained in:
Patrick Pollock
2023-10-10 13:05:52 -06:00
committed by GitHub
parent c9994f9dd2
commit 22834acc98
3 changed files with 44 additions and 13 deletions

View File

@@ -16,6 +16,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <stdarg.h>
#include <math.h>
#include "input.h"
#include "user_io.h"
@@ -1198,7 +1199,9 @@ typedef struct
char id[80];
char name[128];
char sysfs[512];
int max_range;
int ss_range;
int max_cardinal;
float max_range;
} devInput;
static devInput input[NUMDEV] = {};
@@ -2088,14 +2091,25 @@ static void joy_analog(int dev, int axis, int offset, int stick = 0)
int y = pos[stick][num][1];
if (is_n64() && stick == 0)
{
// Update maximum observed cardinal distance
const int abs_x = abs(x);
const int abs_y = abs(y);
if (abs_x > input[dev].max_range) input[dev].max_range = abs_x;
if (abs_y > input[dev].max_range) input[dev].max_range = abs_y;
if (abs_x > input[dev].max_cardinal) input[dev].max_cardinal = abs_x;
if (abs_y > input[dev].max_cardinal) input[dev].max_cardinal = abs_y;
// Update maximum observed diag
// Use sum of squares and only calc sqrt() when necessary
const int ss_range_curr = x*x + y*y;
// compare to max ss_range and update if larger
if ((ss_range_curr > input[dev].ss_range) & (abs(abs_x - abs_y) <= 3))
{
input[dev].ss_range = ss_range_curr;
input[dev].max_range = sqrt(ss_range_curr);
}
// emulate n64 joystick range and shape for regular -127-+127 controllers
n64_joy_emu(x, y, &x, &y, input[dev].max_range);
n64_joy_emu(x, y, &x, &y, input[dev].max_cardinal, input[dev].max_range);
}
if(stick) user_io_r_analog_joystick(num, (char)x, (char)y);
else user_io_l_analog_joystick(num, (char)x, (char)y);

View File

@@ -2,7 +2,13 @@
#include <stdint.h>
#include <math.h>
void n64_joy_emu(int x, int y, int* x2, int* y2, int max_range)
#define N64_MAX_DIAG 69
#define N64_MAX_DIST sqrt(N64_MAX_DIAG * N64_MAX_DIAG * 2)
#define N64_MAX_CARDINAL 85
#define OUTER_DEADZONE 2.0f
#define WEDGE_BOUNDARY (N64_MAX_CARDINAL - 69.0f) / 69.0f
void n64_joy_emu(int x, int y, int* x2, int* y2, int max_cardinal, float max_range)
{
// Move to top right quadrant to standardize solutions
const int x_flip = x < 0 ? -1 : 1;
@@ -10,10 +16,15 @@ void n64_joy_emu(int x, int y, int* x2, int* y2, int max_range)
const int abs_x = x * x_flip;
const int abs_y = y * y_flip;
// Reduce range to radius 97.5807358037f ((69,69) diagonal of original controller)
// Either reduce range to radius 97.5807358037f ((69,69) diagonal of original controller)
// or reduce cardinals to 85, whichever is less aggressive (smaller reduction in scaling)
// (subtracts 2 from each to allow for minor outer deadzone)
// assumes the max range is at least 85 (max cardinal of original controller)
if (max_range < 85) max_range = 85;
float scale = 97.5807358037f / max_range;
if (max_cardinal < N64_MAX_CARDINAL) max_cardinal = N64_MAX_CARDINAL;
if (max_range < N64_MAX_DIST) max_range = N64_MAX_DIST;
float scale_cardinal = N64_MAX_CARDINAL / (max_cardinal - OUTER_DEADZONE);
float scale_range = N64_MAX_DIST / (max_range - OUTER_DEADZONE);
float scale = scale_cardinal > scale_range ? scale_cardinal : scale_range;
float scaled_x = abs_x * scale;
float scaled_y = abs_y * scale;
@@ -31,14 +42,14 @@ void n64_joy_emu(int x, int y, int* x2, int* y2, int max_range)
// Clamp scaled_min and scaled_max
// Note: wedge boundary is given by x = 85 - y * ((85 - 69) / 69)
// If x + y * (16 / 69) > 85, coordinates exceed boundary and need clamped
float boundary = scaled_max + scaled_min * 0.231884057971f;
if (boundary > 85) {
float boundary = scaled_max + scaled_min * WEDGE_BOUNDARY;
if (boundary > N64_MAX_CARDINAL) {
// We know target value is on:
// 1) Boundary line: x = 85 - y * (16 / 69)
// 2) Observed slope line: y = (scaled_max / scaled_min) * x
// Solving system of equations yields:
scaled_min = 85 * scaled_min / boundary;
scaled_max = 85 - scaled_min * 0.231884057971f; // Boundary line
scaled_min = N64_MAX_CARDINAL * scaled_min / boundary;
scaled_max = N64_MAX_CARDINAL - scaled_min * WEDGE_BOUNDARY; // Boundary line
}
// Move back from wedge to actual coordinates
@@ -50,3 +61,9 @@ void n64_joy_emu(int x, int y, int* x2, int* y2, int max_range)
*y2 = y_flip * scaled_max;
}
}
#undef N64_MAX_DIAG
#undef N64_MAX_DIST
#undef N64_MAX_CARDINAL
#undef OUTER_DEADZONE
#undef WEDGE_BOUNDARY

View File

@@ -1 +1 @@
void n64_joy_emu(int x, int y, int* x2, int* y2, int max_range);
void n64_joy_emu(int x, int y, int* x2, int* y2, int max_cardinal, float max_range);