Skip to content

Commit fa75d56

Browse files
committed
Fix stamina wheel being invisible in first-person
1 parent 3e30ec3 commit fa75d56

File tree

6 files changed

+145
-4
lines changed

6 files changed

+145
-4
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ While more integrated solutions are being found out, there's probably ways to se
3434

3535
### Current Limitations & Known Issues
3636

37-
- Stamina wheel is difficult to see after any sorts of camera rotation.
3837
- There's a very small chance that the screen stays black after exiting any menus, which requires restarting the game to continue.
3938
- Bow Aiming is done via a crosshair on the VR headset. Bow support might be added at some point.
40-
- No roomscale support. You can freely move around your room, but enemies and physics will use your center point.
39+
- No roomscale support. You can freely move around your room, but enemies and physics will use your center point.
4140
- Magnesis & Stasis aim is off-center at far distances. Point your gaze to the **right** of the object to highlight it.
4241
- Climbing ladders require jumping up the ladder to go up and you have to look at the ladder.
4342
- You can get stuck behind ladders sometimes, especially when you stop moving at the very top of the ladder while climbing down. So keep moving at the start!
44-
- Bombs, barrels etc. are thrown at a weird angle.
43+
- Bombs, barrels etc. are thrown at a weird angle.
4544
- It'll lack some comfort options for now, like a left-handed mode or snap turning. These will be added later.
4645

4746
### Mod Installation

include/game_structs.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,25 @@ struct BESeadLookAtCamera : BESeadCamera {
534534
static_assert(sizeof(BESeadCamera) == 0x34, "BESeadCamera size mismatch");
535535
static_assert(sizeof(BESeadLookAtCamera) == 0x58, "BESeadLookAtCamera size mismatch");
536536

537+
538+
struct UIManagerInnerArray {
539+
PADDED_BYTES(0x00, 0x34);
540+
BEVec3 uiPos1;
541+
BEVec3 uiPos2;
542+
PADDED_BYTES(0x50, 0x40668-0x04);
543+
};
544+
struct UIManager {
545+
PADDED_BYTES(0x00, 0x1C);
546+
UIManagerInnerArray innerArray;
547+
PADDED_BYTES(0x40688, 0x40DD0-0x04);
548+
};
549+
550+
static_assert(offsetof(UIManager, innerArray) == 0x20, "UIManager.innerArray offset mismatch");
551+
static_assert(offsetof(UIManager, innerArray.uiPos1) == 0x58, "UIManagerInnerArray.uiPos1 offset mismatch");
552+
static_assert(offsetof(UIManager, innerArray.uiPos2) == 0x64, "UIManagerInnerArray.uiPos1 offset mismatch");
553+
static_assert(sizeof(UIManagerInnerArray) == 0x40668, "UIManagerInnerArray size mismatch");
554+
static_assert(sizeof(UIManager) == 0x40DD0, "UIManager size mismatch");
555+
537556
// not identical memory layout wise
538557
struct Frustum {
539558
glm::vec4 planes[6];

include/pch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ struct BEVec2 : BETypeCompatible {
315315
BEVec2() = default;
316316
BEVec2(float x, float y): x(x), y(y) {}
317317
BEVec2(BEType<float> x, BEType<float> y): x(x), y(y) {}
318+
319+
glm::fvec2 getLE() const {
320+
return { x.getLE(), y.getLE() };
321+
}
318322
};
319323

320324
struct BEVec3 : BETypeCompatible {

resources/BreathOfTheWild_BetterVR/patch_RND_StereoRendering.asm

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,4 +1106,60 @@ blr
11061106
0x02C43450 = bla custom_ModifyProjectionUsingCamera
11071107

11081108
;0x0318FE7C = ba custom_ModifyProjectionUsingCamera
1109-
;0x0318FEFC = ba import.coreinit.hook_ModifyProjectionUsingCamera
1109+
;0x0318FEFC = ba import.coreinit.hook_ModifyProjectionUsingCamera
1110+
1111+
1112+
; =================================================================================
1113+
1114+
1115+
1116+
hook_updateUIPosition:
1117+
lis r10, currentEyeSide@ha
1118+
lwz r10, currentEyeSide@l(r10)
1119+
lis r11, currentFrameCounter@ha
1120+
lwz r11, currentFrameCounter@l(r11)
1121+
; r12 holds uiManager instance
1122+
ba import.coreinit.hook_UpdateUIPosition
1123+
blr
1124+
1125+
;0x03078ACC = ba hook_updateUIPosition
1126+
1127+
;0x03060CD0 = blr
1128+
;0x03A3BCC8 = blr
1129+
1130+
1131+
; =================================================================================
1132+
1133+
0x02FB2468 = orig_StaminaGaugeScreenPositionFn:
1134+
1135+
hook_fixStaminaGaugeToScreenPosition:
1136+
mflr r0
1137+
stwu r1, -0x20(r1)
1138+
stw r0, 0x24(r1)
1139+
stw r3, 0x1C(r1)
1140+
stw r4, 0x18(r1)
1141+
stw r5, 0x14(r1)
1142+
1143+
lis r3, orig_StaminaGaugeScreenPositionFn@ha
1144+
addi r3, r3, orig_StaminaGaugeScreenPositionFn@l
1145+
mtctr r3
1146+
lwz r3, 0x1C(r1)
1147+
bctrl ; call original function to get the original screen position
1148+
1149+
lwz r4, 0x18(r1) ; r4 is a pointer to the 2D screen position that we need to overwrite
1150+
lwz r5, 0x14(r1) ; r5 is a pointer to the world position
1151+
bla import.coreinit.hook_FixStaminaGaugeScreenPosition
1152+
1153+
lwz r5, 0x14(r1)
1154+
lwz r4, 0x18(r1)
1155+
lwz r3, 0x1C(r1)
1156+
lwz r0, 0x24(r1)
1157+
addi r1, r1, 0x20
1158+
mtlr r0
1159+
blr
1160+
1161+
; replace the player pos to screen position with static screen coords
1162+
0x02FB2608 = bla hook_fixStaminaGaugeToScreenPosition
1163+
1164+
; force extra stamina gauge icons to just stay in place instead of dynamically hanging onto the regular stamina wheel once it appears
1165+
0x02FB2760 = bla import.coreinit.hook_FixExtraStaminaGaugeIconPositions

src/hooking/camera.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,65 @@ void CemuHooks::hook_UpdateCameraForGameplay(PPCInterpreter_t* hCPU) {
193193
s_framesSinceLastCameraUpdate = 0;
194194
}
195195

196+
// turns out this only does the minimap ui, and not the stamina UI :/
197+
//void CemuHooks::hook_UpdateUIPosition(PPCInterpreter_t* hCPU) {
198+
// hCPU->instructionPointer = hCPU->sprNew.LR;
199+
//
200+
// EyeSide side = hCPU->gpr[10] == 0 ? EyeSide::LEFT : EyeSide::RIGHT;
201+
// uint32_t currFrameCounter = hCPU->gpr[11];
202+
// uint32_t doesUIManagerExist = hCPU->gpr[3] != 0;
203+
// uint32_t uiManagerInstance = hCPU->gpr[12];
204+
//
205+
// if (!doesUIManagerExist) {
206+
// return;
207+
// }
208+
//
209+
// BEVec3 playerPosCopy = getMemory<BEVec3>(uiManagerInstance + offsetof(UIManager, innerArray.uiPos1));
210+
// BEVec3 playerMtxPositionCopy = getMemory<BEVec3>(uiManagerInstance + offsetof(UIManager, innerArray.uiPos2));
211+
//
212+
// Log::print<INFO>("[{}] Updating UI position (frame = {}, playerPos = {}, playerMtxPos = {})", side, currFrameCounter, playerPosCopy, playerMtxPositionCopy);
213+
//
214+
// writeMemory(uiManagerInstance + offsetof(UIManager, innerArray.uiPos1), &playerPosCopy);
215+
// writeMemory(uiManagerInstance + offsetof(UIManager, innerArray.uiPos2), &playerMtxPositionCopy);
216+
//}
217+
218+
void CemuHooks::hook_FixStaminaGaugeScreenPosition(PPCInterpreter_t* hCPU) {
219+
hCPU->instructionPointer = hCPU->sprNew.LR;
220+
221+
if (IsThirdPerson()) {
222+
return;
223+
}
224+
225+
BEVec2 staminaGauge2DPos;
226+
readMemory(hCPU->gpr[4], &staminaGauge2DPos);
227+
BEVec3 playerPosToCameraPos;
228+
readMemory(hCPU->gpr[5], &playerPosToCameraPos);
229+
230+
Log::print<PPC>("Fixing stamina gauge position (oldPos = {}, playerPosToCameraPos = {})", staminaGauge2DPos.getLE(), playerPosToCameraPos);
231+
232+
staminaGauge2DPos = BEVec2{ -482.0f, +170.0f }; // nested under hearts
233+
//staminaGauge2DPos = BEVec2{ 380.0f, 300.0f }; // above the status bars but at the top of the screen
234+
//staminaGauge2DPos = BEVec2{ -220.0f, 300.0f }; // above the status bars but at the top of the screen
235+
playerPosToCameraPos = BEVec3{ 0.0f, 0.0, 0.0f };
236+
237+
writeMemory(hCPU->gpr[4], &staminaGauge2DPos);
238+
writeMemory(hCPU->gpr[5], &playerPosToCameraPos);
239+
}
240+
241+
void CemuHooks::hook_FixExtraStaminaGaugeIconPositions(PPCInterpreter_t* hCPU) {
242+
hCPU->instructionPointer = hCPU->sprNew.LR;
243+
244+
// original instruction that got replaced
245+
hCPU->fpr[29].fp0 = 1.0f;
246+
247+
if (IsThirdPerson()) {
248+
return;
249+
}
250+
251+
// manually jump
252+
hCPU->instructionPointer = 0x02FB29C4;
253+
}
254+
196255
glm::mat4 CemuHooks::s_lastCameraMtx = glm::mat4(1.0f);
197256

198257
void CemuHooks::hook_GetRenderCamera(PPCInterpreter_t* hCPU) {

src/hooking/cemu_hooks.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class CemuHooks {
4646
osLib_registerHLEFunction("coreinit", "hook_PlayerLadderFix", &hook_PlayerLadderFix);
4747
osLib_registerHLEFunction("coreinit", "hook_PlayerIsRiding", &hook_PlayerIsRiding);
4848
osLib_registerHLEFunction("coreinit", "hook_PlayerIsRidingSandSeal", &hook_PlayerIsRidingSandSeal);
49+
osLib_registerHLEFunction("coreinit", "hook_FixStaminaGaugeScreenPosition", &hook_FixStaminaGaugeScreenPosition);
50+
osLib_registerHLEFunction("coreinit", "hook_FixExtraStaminaGaugeIconPositions", &hook_FixExtraStaminaGaugeIconPositions);
4951

5052
// First-Person Model Hooks
5153
osLib_registerHLEFunction("coreinit", "hook_SetActorOpacity", &hook_SetActorOpacity);
@@ -246,6 +248,8 @@ class CemuHooks {
246248
static void hook_CalculateModelOpacity(PPCInterpreter_t* hCPU);
247249
static void hook_ModifyBoneMatrix(PPCInterpreter_t* hCPU);
248250
static void hook_ChangeWeaponMtx(PPCInterpreter_t* hCPU);
251+
static void hook_FixStaminaGaugeScreenPosition(PPCInterpreter_t* hCPU);
252+
static void hook_FixExtraStaminaGaugeIconPositions(PPCInterpreter_t* hCPU);
249253

250254
// First-Person Weapon Hooks
251255
static void hook_EquipWeapon(PPCInterpreter_t* hCPU);

0 commit comments

Comments
 (0)