control rumble intensity to mimic n64 rumble pak & separate rumble from input
This commit is contained in:
parent
fa8092b70e
commit
51759611e1
|
@ -118,6 +118,7 @@ namespace recomp {
|
||||||
|
|
||||||
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
||||||
void set_rumble(bool);
|
void set_rumble(bool);
|
||||||
|
void update_rumble();
|
||||||
void handle_events();
|
void handle_events();
|
||||||
|
|
||||||
// Rumble strength ranges from 0 to 100.
|
// Rumble strength ranges from 0 to 100.
|
||||||
|
|
|
@ -13,16 +13,108 @@ void recomp_set_current_frame_poll_id();
|
||||||
void PadMgr_HandleRetrace(void);
|
void PadMgr_HandleRetrace(void);
|
||||||
void PadMgr_LockPadData(void);
|
void PadMgr_LockPadData(void);
|
||||||
void PadMgr_UnlockPadData(void);
|
void PadMgr_UnlockPadData(void);
|
||||||
|
void PadMgr_UpdateRumble(void);
|
||||||
|
void PadMgr_UpdateConnections(void);
|
||||||
|
void PadMgr_UpdateInputs(void);
|
||||||
|
void PadMgr_InitVoice(void);
|
||||||
|
OSMesgQueue* PadMgr_AcquireSerialEventQueue(void);
|
||||||
|
void PadMgr_ReleaseSerialEventQueue(OSMesgQueue* serialEventQueue);
|
||||||
|
|
||||||
void PadMgr_ThreadEntry() {
|
|
||||||
// @recomp Controller polling was moved to the main thread, so there's nothing to do here.
|
extern PadMgr* sPadMgrInstance;
|
||||||
|
extern s32 sPadMgrRetraceCount;
|
||||||
|
extern FaultMgr gFaultMgr;
|
||||||
|
extern s32 sVoiceInitStatus;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* 0 */ VOICE_INIT_FAILED, // voice initialization failed
|
||||||
|
/* 1 */ VOICE_INIT_TRY, // try to initialize voice
|
||||||
|
/* 2 */ VOICE_INIT_SUCCESS // voice initialized
|
||||||
|
} VoiceInitStatus;
|
||||||
|
|
||||||
|
void recomp_update_rumble();
|
||||||
|
|
||||||
|
void PadMgr_HandleRetrace(void) {
|
||||||
|
// Execute rumble callback
|
||||||
|
if (sPadMgrInstance->rumbleRetraceCallback != NULL) {
|
||||||
|
sPadMgrInstance->rumbleRetraceCallback(sPadMgrInstance->rumbleRetraceArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and initialize a Voice Recognition Unit if not already attempted
|
||||||
|
if (sVoiceInitStatus != VOICE_INIT_FAILED) {
|
||||||
|
PadMgr_InitVoice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rumble Pak
|
||||||
|
if (gFaultMgr.msgId != 0) {
|
||||||
|
// If fault is active, no rumble
|
||||||
|
PadMgr_RumbleStop();
|
||||||
|
} else if (sPadMgrInstance->rumbleOffTimer > 0) {
|
||||||
|
// If the rumble off timer is active, no rumble
|
||||||
|
--sPadMgrInstance->rumbleOffTimer;
|
||||||
|
PadMgr_RumbleStop();
|
||||||
|
} else if (sPadMgrInstance->rumbleOnTimer == 0) {
|
||||||
|
// If the rumble on timer is inactive, no rumble
|
||||||
|
PadMgr_RumbleStop();
|
||||||
|
} else if (!sPadMgrInstance->isResetting) {
|
||||||
|
// If not resetting, update rumble
|
||||||
|
PadMgr_UpdateRumble();
|
||||||
|
--sPadMgrInstance->rumbleOnTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
recomp_update_rumble();
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll_inputs(void) {
|
||||||
|
OSMesgQueue* serialEventQueue = PadMgr_AcquireSerialEventQueue();
|
||||||
|
// Begin reading controller data
|
||||||
|
osContStartReadData(serialEventQueue);
|
||||||
|
|
||||||
|
// Wait for controller data
|
||||||
|
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
osContGetReadData(sPadMgrInstance->pads);
|
||||||
|
|
||||||
|
// Clear all but controller 1
|
||||||
|
bzero(&sPadMgrInstance->pads[1], sizeof(*sPadMgrInstance->pads) * (MAXCONTROLLERS - 1));
|
||||||
|
|
||||||
|
// If in PreNMI, clear all controllers
|
||||||
|
if (sPadMgrInstance->isResetting) {
|
||||||
|
bzero(sPadMgrInstance->pads, sizeof(sPadMgrInstance->pads));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query controller statuses
|
||||||
|
osContStartQuery(serialEventQueue);
|
||||||
|
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
osContGetQuery(sPadMgrInstance->padStatus);
|
||||||
|
|
||||||
|
// Lock serial message queue
|
||||||
|
PadMgr_ReleaseSerialEventQueue(serialEventQueue);
|
||||||
|
|
||||||
|
// Update connections
|
||||||
|
PadMgr_UpdateConnections();
|
||||||
|
|
||||||
|
// Lock input data
|
||||||
|
PadMgr_LockPadData();
|
||||||
|
|
||||||
|
// Update input data
|
||||||
|
PadMgr_UpdateInputs();
|
||||||
|
|
||||||
|
// Execute input callback
|
||||||
|
if (sPadMgrInstance->inputRetraceCallback != NULL) {
|
||||||
|
sPadMgrInstance->inputRetraceCallback(sPadMgrInstance->inputRetraceArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock input data
|
||||||
|
PadMgr_UnlockPadData();
|
||||||
|
sPadMgrRetraceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @recomp Patched to do the actual input polling.
|
// @recomp Patched to do the actual input polling.
|
||||||
void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
|
void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
|
||||||
// @recomp Do an actual poll if gameRequest is true.
|
// @recomp Do an actual poll if gameRequest is true.
|
||||||
if (gameRequest) {
|
if (gameRequest) {
|
||||||
PadMgr_HandleRetrace();
|
poll_inputs();
|
||||||
// @recomp Tag the current frame's input polling id for latency tracking.
|
// @recomp Tag the current frame's input polling id for latency tracking.
|
||||||
recomp_set_current_frame_poll_id();
|
recomp_set_current_frame_poll_id();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
#define osFlashWriteArray osFlashWriteArray_recomp
|
#define osFlashWriteArray osFlashWriteArray_recomp
|
||||||
#define osFlashWriteBuffer osFlashWriteBuffer_recomp
|
#define osFlashWriteBuffer osFlashWriteBuffer_recomp
|
||||||
#define osWritebackDCache osWritebackDCache_recomp
|
#define osWritebackDCache osWritebackDCache_recomp
|
||||||
|
|
||||||
|
#define osContStartReadData osContStartReadData_recomp
|
||||||
|
#define osContGetReadData osContGetReadData_recomp
|
||||||
|
#define osContStartQuery osContStartQuery_recomp
|
||||||
|
#define osContGetQuery osContGetQuery_recomp
|
||||||
|
|
||||||
#define sinf __sinf_recomp
|
#define sinf __sinf_recomp
|
||||||
#define cosf __cosf_recomp
|
#define cosf __cosf_recomp
|
||||||
#define bzero bzero_recomp
|
#define bzero bzero_recomp
|
||||||
|
|
|
@ -42,3 +42,8 @@ osFlashWriteArray_recomp = 0x8F000060;
|
||||||
osFlashWriteBuffer_recomp = 0x8F000064;
|
osFlashWriteBuffer_recomp = 0x8F000064;
|
||||||
osWritebackDCache_recomp = 0x8F000068;
|
osWritebackDCache_recomp = 0x8F000068;
|
||||||
recomp_get_pending_set_time = 0x8F00006C;
|
recomp_get_pending_set_time = 0x8F00006C;
|
||||||
|
osContStartReadData_recomp = 0x8F000070;
|
||||||
|
osContGetReadData_recomp = 0x8F000074;
|
||||||
|
osContStartQuery_recomp = 0x8F000078;
|
||||||
|
osContGetQuery_recomp = 0x8F00007C;
|
||||||
|
recomp_update_rumble = 0x8F000080;
|
||||||
|
|
|
@ -31,6 +31,8 @@ static struct {
|
||||||
std::array<float, 2> rotation_delta{};
|
std::array<float, 2> rotation_delta{};
|
||||||
std::mutex pending_rotation_mutex;
|
std::mutex pending_rotation_mutex;
|
||||||
std::array<float, 2> pending_rotation_delta{};
|
std::array<float, 2> pending_rotation_delta{};
|
||||||
|
float cur_rumble;
|
||||||
|
bool rumble_active;
|
||||||
} InputState;
|
} InputState;
|
||||||
|
|
||||||
std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT;
|
std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT;
|
||||||
|
@ -370,10 +372,34 @@ void recomp::poll_inputs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void recomp::set_rumble(bool on) {
|
void recomp::set_rumble(bool on) {
|
||||||
uint16_t rumble_strength = recomp::get_rumble_strength() * 0xFFFF / 100;
|
InputState.rumble_active = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float lerp(float from, float to, float amount) {
|
||||||
|
return (from + (to - from) * amount);
|
||||||
|
}
|
||||||
|
static float smoothstep(float from, float to, float amount) {
|
||||||
|
amount = (amount * amount) * (3.0f - 2.0f * amount);
|
||||||
|
return lerp(from, to, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update rumble to attempt to mimic the way n64 rumble ramps up and falls off
|
||||||
|
void recomp::update_rumble() {
|
||||||
|
// Note: values are not accurate! just approximations based on feel
|
||||||
|
if (InputState.rumble_active) {
|
||||||
|
InputState.cur_rumble += 0.17f;
|
||||||
|
if (InputState.cur_rumble > 1) InputState.cur_rumble = 1;
|
||||||
|
} else {
|
||||||
|
InputState.cur_rumble *= 0.92f;
|
||||||
|
InputState.cur_rumble -= 0.01f;
|
||||||
|
if (InputState.cur_rumble < 0) InputState.cur_rumble = 0;
|
||||||
|
}
|
||||||
|
float smooth_rumble = smoothstep(0, 1, InputState.cur_rumble);
|
||||||
|
|
||||||
|
uint16_t rumble_strength = smooth_rumble * (recomp::get_rumble_strength() * 0xFFFF / 100);
|
||||||
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
|
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
|
||||||
for (const auto& controller : InputState.cur_controllers) {
|
for (const auto& controller : InputState.cur_controllers) {
|
||||||
SDL_GameControllerRumble(controller, 0, on ? rumble_strength : 0, duration);
|
SDL_GameControllerRumble(controller, 0, rumble_strength, duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,3 +81,7 @@ extern "C" void recomp_get_low_health_beeps_enabled(uint8_t* rdram, recomp_conte
|
||||||
extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) {
|
||||||
_return(ctx, static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(ultramodern::time_since_start()).count()));
|
_return(ctx, static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(ultramodern::time_since_start()).count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void recomp_update_rumble(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
recomp::update_rumble();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue