From f27ddede59d8134eccf8a106f62eaebeecc4ace5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Sun, 9 Aug 2026 22:01:31 +0000 Subject: [PATCH] Fix mirror decal not rendering on some walls (#7052) --- soh/soh/Enhancements/AlwaysOnFixes.cpp | 15 +++++++++++++++ .../vanilla-behavior/GIVanillaBehavior.h | 8 ++++++++ soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/AlwaysOnFixes.cpp b/soh/soh/Enhancements/AlwaysOnFixes.cpp index 639330f680..66c270c224 100644 --- a/soh/soh/Enhancements/AlwaysOnFixes.cpp +++ b/soh/soh/Enhancements/AlwaysOnFixes.cpp @@ -8,6 +8,7 @@ extern "C" { #include "include/z64camera.h" #include "src/overlays/actors/ovl_En_Test/z_en_test.h" #include "src/overlays/actors/ovl_En_Horse/z_en_horse.h" +#include "src/overlays/actors/ovl_Mir_Ray/z_mir_ray.h" void UnregisterActorSkeletons(struct Actor* actor); extern void Player_UseItem(PlayState*, Player*, s32); extern PlayState* gPlayState; @@ -110,6 +111,20 @@ void RegisterAlwaysOnFixes() { } }); + // Mir_Ray draws the reflection image straight on the collision poly's plane, but CollisionPoly + // stores that plane quantized (s16 normal, integer dist), so for about half of all walls it + // lands a fraction of a unit inside the drawn surface. N64 RDP's decal mode handled that, + // but our graphics pipeline does not. Lift the image off the plane along the poly normal. + COND_VB_SHOULD(VB_MIRRAY_DRAW_REFLECTION, true, { + if (*should) { + MirRayShieldReflection* reflection = va_arg(args, MirRayShieldReflection*); + CollisionPoly* poly = reflection->reflectionPoly; + reflection->pos.x += COLPOLY_GET_NORMAL(poly->normal.x); + reflection->pos.y += COLPOLY_GET_NORMAL(poly->normal.y); + reflection->pos.z += COLPOLY_GET_NORMAL(poly->normal.z); + } + }); + // Handle first person aiming camera settings COND_VB_SHOULD(VB_CHANGE_AIMING_CAMERA, true, { s8* heldItemAction = va_arg(args, s8*); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index f0b91582c1..e41669089d 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1640,6 +1640,14 @@ typedef enum { // - `*EnMd` VB_MIDO_SPAWN, + // #### `result` + // ```c + // reflection[i].reflectionPoly != NULL + // ``` + // #### `args` + // - `*MirRayShieldReflection` + VB_MIRRAY_DRAW_REFLECTION, + // #### `result` // ```c // false diff --git a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c index 334c1fdf6c..ae1b130bd3 100644 --- a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c +++ b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c @@ -7,6 +7,7 @@ #include "z_mir_ray.h" #include "objects/object_mir_ray/object_mir_ray.h" #include "soh/frame_interpolation.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED) @@ -510,7 +511,8 @@ void MirRay_Draw(Actor* thisx, PlayState* play) { } } for (i = 0; i < 6; i++) { - if (reflection[i].reflectionPoly != NULL) { + if (GameInteractor_Should(VB_MIRRAY_DRAW_REFLECTION, reflection[i].reflectionPoly != NULL, + &reflection[i])) { FrameInterpolation_RecordOpenChild(&reflection[i], i); Matrix_Translate(reflection[i].pos.x, reflection[i].pos.y, reflection[i].pos.z, MTXMODE_NEW); Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY);