mirror of
				https://github.com/Zelda64Recomp/Zelda64Recomp.git
				synced 2025-10-30 08:03:03 +00:00 
			
		
		
		
	control rumble intensity to mimic n64 rumble pak & separate rumble from input
This commit is contained in:
		
							parent
							
								
									fa8092b70e
								
							
						
					
					
						commit
						51759611e1
					
				
					 6 changed files with 139 additions and 5 deletions
				
			
		|  | @ -118,6 +118,7 @@ namespace recomp { | |||
| 
 | ||||
|     void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out); | ||||
|     void set_rumble(bool); | ||||
|     void update_rumble(); | ||||
|     void handle_events(); | ||||
|      | ||||
|     // Rumble strength ranges from 0 to 100.
 | ||||
|  |  | |||
|  | @ -13,16 +13,108 @@ void recomp_set_current_frame_poll_id(); | |||
| void PadMgr_HandleRetrace(void); | ||||
| void PadMgr_LockPadData(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.
 | ||||
| void PadMgr_GetInput(Input* inputs, s32 gameRequest) { | ||||
|     // @recomp Do an actual poll if gameRequest is true.
 | ||||
|     if (gameRequest) { | ||||
|         PadMgr_HandleRetrace(); | ||||
|         poll_inputs(); | ||||
|         // @recomp Tag the current frame's input polling id for latency tracking.
 | ||||
|         recomp_set_current_frame_poll_id(); | ||||
|     } | ||||
|  |  | |||
|  | @ -9,6 +9,12 @@ | |||
| #define osFlashWriteArray osFlashWriteArray_recomp | ||||
| #define osFlashWriteBuffer osFlashWriteBuffer_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 cosf __cosf_recomp | ||||
| #define bzero bzero_recomp | ||||
|  |  | |||
|  | @ -42,3 +42,8 @@ osFlashWriteArray_recomp = 0x8F000060; | |||
| osFlashWriteBuffer_recomp = 0x8F000064; | ||||
| osWritebackDCache_recomp = 0x8F000068; | ||||
| 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::mutex pending_rotation_mutex; | ||||
|     std::array<float, 2> pending_rotation_delta{}; | ||||
|     float cur_rumble; | ||||
|     bool rumble_active; | ||||
| } InputState; | ||||
| 
 | ||||
| std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT; | ||||
|  | @ -370,10 +372,34 @@ void recomp::poll_inputs() { | |||
| } | ||||
| 
 | ||||
| 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.
 | ||||
|     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) { | ||||
|     _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…
	
	Add table
		
		Reference in a new issue
	
	 thecozies
						thecozies