summaryrefslogtreecommitdiff
path: root/soh/src/code
diff options
context:
space:
mode:
authorArchez <Archez@users.noreply.github.com>2024-11-09 12:51:31 -0500
committerGitHub <noreply@github.com>2024-11-09 12:51:31 -0500
commitf12a2bbbb7f6201a0bc8787d23cd446a1a2b4d48 (patch)
tree932f88be2e593d76120f22b8b64b006e1b4e1706 /soh/src/code
parent2603b9736632541fcf86d64af17a461f4447fc7a (diff)
Port over decomp updates for vis* and reimplement vismono fb (#4533)
* Port over decomp updates for vis* * move vismono framebuffer handling to file
Diffstat (limited to 'soh/src/code')
-rw-r--r--soh/src/code/code_800ACE70.c97
-rw-r--r--soh/src/code/code_800AD920.c64
-rw-r--r--soh/src/code/game.c88
-rw-r--r--soh/src/code/z_demo.c16
-rw-r--r--soh/src/code/z_play.c14
-rw-r--r--soh/src/code/z_viscvg.c145
-rw-r--r--soh/src/code/z_vismono.c227
-rw-r--r--soh/src/code/z_viszbuf.c116
8 files changed, 474 insertions, 293 deletions
diff --git a/soh/src/code/code_800ACE70.c b/soh/src/code/code_800ACE70.c
deleted file mode 100644
index e22162777..000000000
--- a/soh/src/code/code_800ACE70.c
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "global.h"
-
-// Note : This file is related to z_vismono, the original name was probably z_vis<something before "mono"
-// alphabetically>
-
-Gfx D_8012AC00[] = {
- gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | G_RM_VISCVG | G_RM_VISCVG2),
- gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
- gsDPPipeSync(),
- gsDPSetBlendColor(0, 0, 0, 8),
- gsSPEndDisplayList(),
-};
-
-Gfx D_8012AC28[] = {
- gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
- GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM) |
- GBL_c2(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM)),
- gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
- gsSPEndDisplayList(),
-};
-
-Gfx D_8012AC40[] = {
- gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
- GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) |
- GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)),
-
- gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
- gsSPEndDisplayList(),
-};
-
-Gfx D_8012AC58[] = {
- gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE),
- gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_DISABLE | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2),
- gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
- gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
- GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) |
- GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)),
- gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
- gsSPEndDisplayList(),
-};
-
-// Init
-void func_800ACE70(struct_801664F0* this) {
- this->type = 0;
- this->setScissor = false;
- this->color.r = 255;
- this->color.g = 255;
- this->color.b = 255;
- this->color.a = 255;
-}
-
-// Destroy
-void func_800ACE90(struct_801664F0* this) {
-}
-
-// Draw
-void func_800ACE98(struct_801664F0* this, Gfx** gfxp) {
- Gfx* gfx = *gfxp;
-
- gDPPipeSync(gfx++);
- gDPSetPrimDepth(gfx++, -1, -1);
-
- if (this->setScissor == true) {
- gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- }
-
- switch (this->type) {
- case 1:
- gSPDisplayList(gfx++, D_8012AC40);
- break;
- case 2:
- gDPSetColor(gfx++, G_SETPRIMCOLOR, this->color.rgba);
- gSPDisplayList(gfx++, D_8012AC58);
- break;
- case 3:
- gDPSetColor(gfx++, G_SETBLENDCOLOR, this->color.rgba);
- gSPDisplayList(gfx++, D_8012AC00);
- break;
- case 4:
- gDPSetColor(gfx++, G_SETFOGCOLOR, this->color.rgba);
- gSPDisplayList(gfx++, D_8012AC28);
- break;
- }
-
- gDPPipeSync(gfx++);
- *gfxp = gfx;
-}
diff --git a/soh/src/code/code_800AD920.c b/soh/src/code/code_800AD920.c
deleted file mode 100644
index 2ef7cf0bb..000000000
--- a/soh/src/code/code_800AD920.c
+++ /dev/null
@@ -1,64 +0,0 @@
-#include "global.h"
-
-// Note : This file is related to z_vismono, the original name was probably z_vis<something after "mono" alphabetically>
-
-// z-buffer
-extern u16 D_0E000000[];
-
-// Init
-void func_800AD920(struct_80166500* this) {
- this->useRgba = false;
- this->setScissor = false;
- this->primColor.r = 255;
- this->primColor.g = 255;
- this->primColor.b = 255;
- this->primColor.a = 255;
- this->envColor.a = 255;
- this->envColor.r = 0;
- this->envColor.g = 0;
- this->envColor.b = 0;
-}
-
-// Destroy
-void func_800AD950(struct_80166500* this) {
-}
-
-// Draw
-void func_800AD958(struct_80166500* this, Gfx** gfxp) {
- Gfx* gfx = *gfxp;
-
- // OTRTODO
-#if 0
- u16* tex = D_0E000000;
- s32 fmt = this->useRgba == false ? G_IM_FMT_IA : G_IM_FMT_RGBA;
- s32 y;
- s32 height = 6;
-
- gDPPipeSync(gfx++);
- if (this->setScissor == true) {
- gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- }
-
- gDPSetOtherMode(gfx++,
- G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE |
- G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2);
- gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT,
- PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT);
-
- gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba);
- gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba);
-
- for (y = 0; y <= SCREEN_HEIGHT - height; y += height) {
- gDPLoadTextureBlock(gfx++, tex, fmt, G_IM_SIZ_16b, SCREEN_WIDTH, height, 0, G_TX_NOMIRROR | G_TX_CLAMP,
- G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
-
- gSPTextureRectangle(gfx++, 0, (y) << 2, (SCREEN_WIDTH << 2), (y + height) << 2, G_TX_RENDERTILE, 0, 0,
- (1 << 10), (1 << 10));
- tex += SCREEN_WIDTH * height;
- }
-
- gDPPipeSync(gfx++);
- *gfxp = gfx;
-#endif
-}
diff --git a/soh/src/code/game.c b/soh/src/code/game.c
index e1974ed98..90438b5db 100644
--- a/soh/src/code/game.c
+++ b/soh/src/code/game.c
@@ -6,9 +6,9 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
SpeedMeter D_801664D0;
-struct_801664F0 D_801664F0;
-struct_80166500 D_80166500;
-VisMono sMonoColors;
+VisCvg sVisCvg;
+VisZBuf sVisZBuf;
+VisMono sVisMono;
ViMode sViMode;
FaultClient sGameFaultClient;
u16 sLastButtonPressed;
@@ -31,41 +31,43 @@ void GameState_FaultPrint(void) {
}
}
-void GameState_SetFBFilter(Gfx** gfx) {
- Gfx* gfxP;
- gfxP = *gfx;
-
- if ((R_FB_FILTER_TYPE > 0) && (R_FB_FILTER_TYPE < 5)) {
- D_801664F0.type = R_FB_FILTER_TYPE;
- D_801664F0.color.r = R_FB_FILTER_PRIM_COLOR(0);
- D_801664F0.color.g = R_FB_FILTER_PRIM_COLOR(1);
- D_801664F0.color.b = R_FB_FILTER_PRIM_COLOR(2);
- D_801664F0.color.a = R_FB_FILTER_A;
- func_800ACE98(&D_801664F0, &gfxP);
- } else if ((R_FB_FILTER_TYPE == 5) || (R_FB_FILTER_TYPE == 6)) {
- D_80166500.useRgba = (R_FB_FILTER_TYPE == 6);
- D_80166500.primColor.r = R_FB_FILTER_PRIM_COLOR(0);
- D_80166500.primColor.g = R_FB_FILTER_PRIM_COLOR(1);
- D_80166500.primColor.b = R_FB_FILTER_PRIM_COLOR(2);
- D_80166500.primColor.a = R_FB_FILTER_A;
- D_80166500.envColor.r = R_FB_FILTER_ENV_COLOR(0);
- D_80166500.envColor.g = R_FB_FILTER_ENV_COLOR(1);
- D_80166500.envColor.b = R_FB_FILTER_ENV_COLOR(2);
- D_80166500.envColor.a = R_FB_FILTER_A;
- func_800AD958(&D_80166500, &gfxP);
- } else if (R_FB_FILTER_TYPE == 7) {
- sMonoColors.unk_00 = 0;
- sMonoColors.primColor.r = R_FB_FILTER_PRIM_COLOR(0);
- sMonoColors.primColor.g = R_FB_FILTER_PRIM_COLOR(1);
- sMonoColors.primColor.b = R_FB_FILTER_PRIM_COLOR(2);
- sMonoColors.primColor.a = R_FB_FILTER_A;
- sMonoColors.envColor.r = R_FB_FILTER_ENV_COLOR(0);
- sMonoColors.envColor.g = R_FB_FILTER_ENV_COLOR(1);
- sMonoColors.envColor.b = R_FB_FILTER_ENV_COLOR(2);
- sMonoColors.envColor.a = R_FB_FILTER_A;
- VisMono_Draw(&sMonoColors, &gfxP);
+void GameState_SetFBFilter(Gfx** gfxP) {
+ Gfx* gfx = *gfxP;
+
+ if ((R_FB_FILTER_TYPE >= FB_FILTER_CVG_RGB) && (R_FB_FILTER_TYPE <= FB_FILTER_CVG_RGB_FOG)) {
+ // Visualize coverage
+ sVisCvg.vis.type = FB_FILTER_TO_CVG_TYPE(R_FB_FILTER_TYPE);
+ sVisCvg.vis.primColor.r = R_FB_FILTER_PRIM_COLOR(0);
+ sVisCvg.vis.primColor.g = R_FB_FILTER_PRIM_COLOR(1);
+ sVisCvg.vis.primColor.b = R_FB_FILTER_PRIM_COLOR(2);
+ sVisCvg.vis.primColor.a = R_FB_FILTER_A;
+ VisCvg_Draw(&sVisCvg, &gfx);
+ } else if ((R_FB_FILTER_TYPE == FB_FILTER_ZBUF_IA) || (R_FB_FILTER_TYPE == FB_FILTER_ZBUF_RGBA)) {
+ // Visualize z-buffer
+ sVisZBuf.vis.type = (R_FB_FILTER_TYPE == FB_FILTER_ZBUF_RGBA);
+ sVisZBuf.vis.primColor.r = R_FB_FILTER_PRIM_COLOR(0);
+ sVisZBuf.vis.primColor.g = R_FB_FILTER_PRIM_COLOR(1);
+ sVisZBuf.vis.primColor.b = R_FB_FILTER_PRIM_COLOR(2);
+ sVisZBuf.vis.primColor.a = R_FB_FILTER_A;
+ sVisZBuf.vis.envColor.r = R_FB_FILTER_ENV_COLOR(0);
+ sVisZBuf.vis.envColor.g = R_FB_FILTER_ENV_COLOR(1);
+ sVisZBuf.vis.envColor.b = R_FB_FILTER_ENV_COLOR(2);
+ sVisZBuf.vis.envColor.a = R_FB_FILTER_A;
+ VisZBuf_Draw(&sVisZBuf, &gfx);
+ } else if (R_FB_FILTER_TYPE == FB_FILTER_MONO) {
+ // Monochrome filter
+ sVisMono.vis.type = 0;
+ sVisMono.vis.primColor.r = R_FB_FILTER_PRIM_COLOR(0);
+ sVisMono.vis.primColor.g = R_FB_FILTER_PRIM_COLOR(1);
+ sVisMono.vis.primColor.b = R_FB_FILTER_PRIM_COLOR(2);
+ sVisMono.vis.primColor.a = R_FB_FILTER_A;
+ sVisMono.vis.envColor.r = R_FB_FILTER_ENV_COLOR(0);
+ sVisMono.vis.envColor.g = R_FB_FILTER_ENV_COLOR(1);
+ sVisMono.vis.envColor.b = R_FB_FILTER_ENV_COLOR(2);
+ sVisMono.vis.envColor.a = R_FB_FILTER_A;
+ VisMono_Draw(&sVisMono, &gfx);
}
- *gfx = gfxP;
+ *gfxP = gfx;
}
void func_800C4344(GameState* gameState) {
@@ -420,9 +422,9 @@ void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* g
startTime = endTime;
LOG_CHECK_NULL_POINTER("this->cleanup", gameState->destroy);
- func_800ACE70(&D_801664F0);
- func_800AD920(&D_80166500);
- VisMono_Init(&sMonoColors);
+ VisCvg_Init(&sVisCvg);
+ VisZBuf_Init(&sVisZBuf);
+ VisMono_Init(&sVisMono);
if (SREG(48) == 0) {
ViMode_Init(&sViMode);
}
@@ -450,9 +452,9 @@ void GameState_Destroy(GameState* gameState) {
}
func_800AA0F0();
SpeedMeter_Destroy(&D_801664D0);
- func_800ACE90(&D_801664F0);
- func_800AD950(&D_80166500);
- VisMono_Destroy(&sMonoColors);
+ VisCvg_Destroy(&sVisCvg);
+ VisZBuf_Destroy(&sVisZBuf);
+ VisMono_Destroy(&sVisMono);
if (SREG(48) == 0) {
ViMode_Destroy(&sViMode);
}
diff --git a/soh/src/code/z_demo.c b/soh/src/code/z_demo.c
index 91d9271ff..5c2348829 100644
--- a/soh/src/code/z_demo.c
+++ b/soh/src/code/z_demo.c
@@ -350,16 +350,16 @@ void func_80064824(PlayState* play, CutsceneContext* csCtx, CsCmdBase* cmd) {
Flags_SetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER);
break;
case 22:
- D_801614B0.r = 255;
- D_801614B0.g = 255;
- D_801614B0.b = 255;
- D_801614B0.a = 255;
+ gVisMonoColor.r = 255;
+ gVisMonoColor.g = 255;
+ gVisMonoColor.b = 255;
+ gVisMonoColor.a = 255;
break;
case 23:
- D_801614B0.r = 255;
- D_801614B0.g = 180;
- D_801614B0.b = 100;
- D_801614B0.a = 255.0f * temp;
+ gVisMonoColor.r = 255;
+ gVisMonoColor.g = 180;
+ gVisMonoColor.b = 100;
+ gVisMonoColor.a = 255.0f * temp;
break;
case 24:
play->roomCtx.curRoom.segment = NULL;
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index e7603040d..83bffcd27 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -21,7 +21,7 @@
TransitionUnk sTrnsnUnk;
s32 gTrnsnUnkState;
VisMono gPlayVisMono;
-Color_RGBA8_u32 D_801614B0;
+Color_RGBA8_u32 gVisMonoColor;
FaultClient D_801614B8;
@@ -567,7 +567,7 @@ void Play_Init(GameState* thisx) {
TransitionFade_SetColor(&play->transitionFade, RGBA8(160, 160, 160, 255));
TransitionFade_Start(&play->transitionFade);
VisMono_Init(&gPlayVisMono);
- D_801614B0.a = 0;
+ gVisMonoColor.a = 0;
Flags_UnsetAllEnv(play);
osSyncPrintf("ZELDA ALLOC SIZE=%x\n", THA_GetSize(&play->state.tha));
@@ -1422,11 +1422,9 @@ void Play_Draw(PlayState* play) {
TransitionFade_Draw(&play->transitionFade, &gfxP);
- if (D_801614B0.a > 0) {
- // gPlayVisMono.vis.primColor.rgba = D_801614B0.rgba;
- // VisMono_Draw(&gPlayVisMono, &gfxP);
- gDPSetGrayscaleColor(gfxP++, D_801614B0.r, D_801614B0.g, D_801614B0.b, D_801614B0.a);
- gSPGrayscale(gfxP++, true);
+ if (gVisMonoColor.a > 0) {
+ gPlayVisMono.vis.primColor.rgba = gVisMonoColor.rgba;
+ VisMono_Draw(&gPlayVisMono, &gfxP);
}
gSPEndDisplayList(gfxP++);
@@ -1610,7 +1608,7 @@ void Play_Draw(PlayState* play) {
play->unk_121C7 = 2;
SREG(33) |= 1;
- // 2S2H [Port] Continue to render the post world for pausing to avoid flashing the HUD
+ // SOH [Port] Continue to render the post world for pausing to avoid flashing the HUD
if (gTrnsnUnkState == 2) {
goto Play_Draw_skip;
}
diff --git a/soh/src/code/z_viscvg.c b/soh/src/code/z_viscvg.c
new file mode 100644
index 000000000..9caf39a3f
--- /dev/null
+++ b/soh/src/code/z_viscvg.c
@@ -0,0 +1,145 @@
+/**
+ * @file z_viscvg.c
+ *
+ * This file implements full-screen frame buffer effects involving the visualization of Coverage in various ways.
+ *
+ * Coverage is roughly how much of a pixel is covered by a primitive; the final coverage for a frame is stored in the
+ * color image alpha component where it is used for antialiasing, see PreRender.c and ยง15 of the programming manual for
+ * details.
+ *
+ * To understand this file, it is helpful to remember that A_MEM is essentially synonymous with coverage, and that
+ * `GBL_c1/2(p, a, m, b)` usually represents the RDP blender calculation `(p * a + m * b)`.
+ * Note the division step that is often included in the blender calculation is omitted; the division is skipped if
+ * force blending (FORCE_BL) is set, which is the case for all render modes used in this file.
+ *
+ * Coverage is full when not on an edge, while on an edge it is usually lower. Since coverage is treated as an alpha
+ * value, edges of primitives where coverage is lower will show up darker than primitive interiors in all of the
+ * available modes.
+ *
+ * Coverage is abbreviated to "cvg"; "FB RGB" ("framebuffer red/green/blue") is the color the pixel originally had
+ * before the filter is applied.
+ */
+
+#include "global.h"
+
+/**
+ * Draws only coverage: does not retain any of the original pixel RGB, primColor is used as background color.
+ */
+Gfx sCoverageOnlyDL[] = {
+ gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | G_RM_VISCVG | G_RM_VISCVG2),
+ // (blendColor RGB) * (cvg)
+ gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
+ gsDPPipeSync(),
+ gsDPSetBlendColor(0, 0, 0, 8),
+ gsSPEndDisplayList(),
+};
+
+/**
+ * Draws fog + coverage * RGB of pixels
+ *
+ * @bug This easily overflows the blender because the fog value is added to the coverage value.
+ */
+Gfx sCoverageRGBFogDL[] = {
+ gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
+ GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM) |
+ GBL_c2(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM)),
+ // (fog RGB) * (fog alpha) + (FB RGB) * (cvg)
+ gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
+ gsSPEndDisplayList(),
+};
+
+/**
+ * Draws coverage and RGB of pixels
+ */
+Gfx sCoverageRGBDL[] = {
+ gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
+ GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) |
+ GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)),
+ // (FB RGB) * (cvg)
+ gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
+ gsSPEndDisplayList(),
+};
+
+/**
+ * Two stage filtering:
+ *
+ * 1. Apply a uniform color filter by transparently blending primColor with original frame. The "cloud surface"
+ * RenderMode is used to preserve the coverage for the second stage.
+ * 2. Second half is the same as `sCoverageRGBDL`'s, i.e. (RGB from stage 1) * cvg
+ */
+Gfx sCoverageRGBUniformDL[] = {
+ gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE),
+ gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_DISABLE | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2),
+ // stage 1 color = (primColor RGB) * (primColor Alpha) + (FB RGB) * (1 - primColor Alpha)
+ gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
+
+ gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL |
+ GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) |
+ GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)),
+ // final color = (stage 1 RGB) * (cvg)
+ gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1),
+ gsSPEndDisplayList(),
+};
+
+void VisCvg_Init(VisCvg* this) {
+ this->vis.type = FB_FILTER_NONE;
+ this->vis.scissorType = VIS_NO_SETSCISSOR;
+ this->vis.primColor.r = 255;
+ this->vis.primColor.g = 255;
+ this->vis.primColor.b = 255;
+ this->vis.primColor.a = 255;
+}
+
+void VisCvg_Destroy(VisCvg* this) {
+}
+
+void VisCvg_Draw(VisCvg* this, Gfx** gfxP) {
+ Gfx* gfx = *gfxP;
+
+ gDPPipeSync(gfx++);
+ gDPSetPrimDepth(gfx++, 0xFFFF, 0xFFFF);
+
+ if (this->vis.scissorType == VIS_SETSCISSOR) {
+ gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ }
+
+ switch (this->vis.type) {
+ case FB_FILTER_CVG_RGB:
+ gSPDisplayList(gfx++, sCoverageRGBDL);
+ break;
+
+ case FB_FILTER_CVG_RGB_UNIFORM:
+ // Set primitive color for uniform color filter in custom RenderMode
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, this->vis.primColor.rgba);
+ gSPDisplayList(gfx++, sCoverageRGBUniformDL);
+ break;
+
+ case FB_FILTER_CVG_ONLY:
+ // Set background color for G_RM_VISCVG
+ gDPSetColor(gfx++, G_SETBLENDCOLOR, this->vis.primColor.rgba);
+ gSPDisplayList(gfx++, sCoverageOnlyDL);
+ break;
+
+ case FB_FILTER_CVG_RGB_FOG:
+ // Set fog color for custom RenderMode, needs to be close to 0 to not overflow
+ gDPSetColor(gfx++, G_SETFOGCOLOR, this->vis.primColor.rgba);
+ gSPDisplayList(gfx++, sCoverageRGBFogDL);
+ break;
+
+ default:
+ break;
+ }
+
+ gDPPipeSync(gfx++);
+ *gfxP = gfx;
+}
diff --git a/soh/src/code/z_vismono.c b/soh/src/code/z_vismono.c
index c277eb971..a4e77d70d 100644
--- a/soh/src/code/z_vismono.c
+++ b/soh/src/code/z_vismono.c
@@ -1,137 +1,218 @@
-#include "global.h"
-#include <string.h>
-#include <assert.h>
+/**
+ * @file z_vismono.c
+ *
+ * This file implements a full-screen framebuffer effect for desaturating the contents of the framebuffer image.
+ *
+ * Broadly, this effect is achieved by reinterpreting the contents of the RGBA16 color image as indices into an IA16
+ * color palette that converts each color into the desaturated equivalent. More precise details can be found in inline
+ * comments.
+ */
-// (Note: 80 = SCREEN_HEIGHT/3, see VisMono_DrawTexture)
-// This may not have been kept up-to-date with the code, 1+1+1+80*(7+2+2+3)+1+1 makes more sense
-#define DLSIZE (1 + 3 + 1 + 1 + 80 * (7 + 2 + 2 + 3) + 1)
+#include "global.h"
-// framebuffer
+#include <string.h> // memset
+#include <assert.h>
+#include "soh/framebuffer_effects.h"
+
+// Upstream TODO: Replace these ones they are served from other headers
+#define ASSERT(cond, msg, file, line) assert(cond)
+#define GPACK_IA16(i, a) (((i) << 8) | (a))
+
+// Height of the fragments the color frame buffer (CFB) is split into.
+// It is the maximum amount of lines such that all rgba16 SCREEN_WIDTH-long lines fit into
+// the half of TMEM dedicated to color-indexed data.
+#define VISMONO_CFBFRAG_HEIGHT ((TMEM_SIZE / 2) / (SCREEN_WIDTH * G_IM_SIZ_16b_BYTES))
+
+// Maximum size of the dlist written by `VisMono_DesaturateDList`.
+// `VisMono_DesaturateDList` consistently uses `VISMONO_DLSIZE - 2` double words, so this can be 2 less.
+#define VISMONO_DLSIZE (3 + SCREEN_HEIGHT / VISMONO_CFBFRAG_HEIGHT * (7 + 2 + 2 + 3) + 2 + 2)
+
+// How much each color component contributes to the desaturated result.
+// These coefficients are close to what the YUV color space defines Y (luminance) as:
+// https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB
+#define VISMONO_FAC_RED 2
+#define VISMONO_FAC_GREEN 4
+#define VISMONO_FAC_BLUE 1
+#define VISMONO_FAC_NORM (0x1F * VISMONO_FAC_RED + 0x1F * VISMONO_FAC_GREEN + 0x1F * VISMONO_FAC_BLUE)
+
+// color framebuffer
extern u16 D_0F000000[];
void VisMono_Init(VisMono* this) {
memset(this, 0, sizeof(VisMono));
- this->unk_00 = 0;
- this->setScissor = false;
- this->primColor.r = 255;
- this->primColor.g = 255;
- this->primColor.b = 255;
- this->primColor.a = 255;
- this->envColor.r = 0;
- this->envColor.g = 0;
- this->envColor.b = 0;
- this->envColor.a = 0;
+ this->vis.type = 0;
+ this->vis.scissorType = VIS_NO_SETSCISSOR;
+ this->vis.primColor.r = 255;
+ this->vis.primColor.g = 255;
+ this->vis.primColor.b = 255;
+ this->vis.primColor.a = 255;
+ this->vis.envColor.r = 0;
+ this->vis.envColor.g = 0;
+ this->vis.envColor.b = 0;
+ this->vis.envColor.a = 0;
}
void VisMono_Destroy(VisMono* this) {
- SYSTEM_ARENA_FREE_DEBUG(this->monoDl);
+ SYSTEM_ARENA_FREE(this->dList, "../z_vismono.c", 137);
}
-void VisMono_UpdateTexture(VisMono* this, u16* tex) {
+void VisMono_DesaturateTLUT(VisMono* this, u16* tlut) {
s32 i;
for (i = 0; i < 256; i++) {
- tex[i] = ((((i >> 3 & 0x1F) * 2 + (i << 2 & 0x1F) * 4) * 0xFF / 0xD9) << 8) |
- (((i >> 6 & 0x1F) * 4 + (i >> 1 & 0x1F)) * 0xFF / 0xD9);
+ // `tlut[i]` is a IA16 color
+ // `i` corresponds to either byte of a RGBA16 color RRRR_RGGG GGBB_BBBA from the color frame buffer
+
+ // The high byte I (intensity) corresponds to `i` being interpreted as the high byte RRRR_RGGG
+ // I = (RRRRR * FAC_RED + GGG00 * FAC_GREEN) * (255 / FAC_NORM)
+
+ // The low byte A (alpha) corresponds to `i` being interpreted as the low byte GGBB_BBBA
+ // A = (000GG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM)
+
+ // Note: I + A = (RRRRR * FAC_RED + GGGGG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM)
+
+ tlut[i] = GPACK_IA16(
+ ((i >> 3 & 0x1F) * VISMONO_FAC_RED + (i << 2 & 0x1F) * VISMONO_FAC_GREEN) * 255 / VISMONO_FAC_NORM,
+ ((i >> 6 & 0x1F) * VISMONO_FAC_GREEN + (i >> 1 & 0x1F) * VISMONO_FAC_BLUE) * 255 / VISMONO_FAC_NORM);
}
}
-Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx)
-{
-// OTRTODO
-#if 1
+Gfx* VisMono_DesaturateDList(VisMono* this, Gfx* gfx) {
s32 y;
- s32 height = 3;
- //u16* tex = D_0F000000;
- u16* tex = SEG_ADDR(0xF, 0);
+ s32 height = VISMONO_CFBFRAG_HEIGHT;
+ u16* cfbFrag = D_0F000000;
gDPPipeSync(gfx++);
+ // `G_TT_IA16`: use color-indexed images, and IA16 palettes
gDPSetOtherMode(gfx++,
G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_IA16 | G_TL_TILE |
G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_1PRIMITIVE,
- G_AC_NONE | G_ZS_PRIM | GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) | G_RM_CLD_SURF2);
+ G_AC_NONE | G_ZS_PRIM | G_RM_PASS | G_RM_CLD_SURF2);
+ // First color cycle sums texel 1 alpha and texel 0 color
+ // By using IA16 palettes, this means summing A (from the IA16 color texel 1 maps to)
+ // with I (from the IA16 color texel 0 maps to)
gDPSetCombineLERP(gfx++, 1, 0, TEXEL1_ALPHA, TEXEL0, 0, 0, 0, 1, PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, 0,
0, 0, PRIMITIVE);
for (y = 0; y <= SCREEN_HEIGHT - height; y += height) {
- gDPLoadTextureBlock(gfx++, tex, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0,
+ // Load a few lines of the color frame buffer
+ gDPLoadTextureBlock(gfx++, cfbFrag, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0,
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK,
G_TX_NOLOD, G_TX_NOLOD);
- gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0,
- G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
- gDPSetTileSize(gfx++, G_TX_RENDERTILE, (2 << 2), 0, ((SCREEN_WIDTH * 2 + 1) << 2), (2 << 2));
-
- gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, 1, 1, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0,
- G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
- gDPSetTileSize(gfx++, 1, (1 << 2), 0, ((SCREEN_WIDTH * 2) << 2), (2 << 2));
-
- gSPTextureRectangle(gfx++, 0, y << 2, (SCREEN_WIDTH << 2), (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0,
- 2 << 10, 1 << 10);
- tex += SCREEN_WIDTH * height;
+ // Set texel 0 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT`
+ // Its position in texture image space is shifted along +S by 2
+ gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, G_TX_RENDERTILE,
+ 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
+ gDPSetTileSize(gfx++, G_TX_RENDERTILE, 2 << 2, 0, (SCREEN_WIDTH * 2 + 1) << 2,
+ (VISMONO_CFBFRAG_HEIGHT - 1) << 2);
+
+ // Set texel 1 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT`
+ // Its position in texture image space is shifted along +S by 1
+ // Note the palette index for this tile has also been incremented from 0 to 1, however the palette index is
+ // ignored for CI8 texture sampling.
+ gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, 1, 1,
+ G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0);
+ gDPSetTileSize(gfx++, 1, 1 << 2, 0, (SCREEN_WIDTH * 2) << 2, (VISMONO_CFBFRAG_HEIGHT - 1) << 2);
+
+ // Draw a `SCREEN_WIDTH` wide, `height` high rectangle.
+ // Texture coordinate T (vertical) starts at 0 and changes by one each line (dtdy = 1)
+ // Texture coordinate S (horizontal) starts at 2 and changes by two each column (dsdx = 2)
+
+ // Because texel 0 is shifted by 2 and texel 1 only by 1 along +S,
+ // a pixel at S coordinates s = 2+2*n will look at the 2*n-th byte of texel 0 and the 2*n+1-th byte of texel 1.
+ // (in "s = 2+2*n" the first "2" is the starting S coordinate and the second "2" is the dsdx value)
+
+ // The 2*n-th byte of texel 0 is the high byte of the n-th RGBA16 color of the color frame buffer.
+ // The 2*n+1-th byte of texel 1 is the low byte of the n-th RGBA16 color of the color frame buffer.
+
+ // With the TLUT computed by `VisMono_DesaturateTLUT`:
+ // The 2*n-th byte of texel 0 maps to a IA16 color where the high byte I (intensity) corresponds to
+ // the high byte of the n-th RGBA16 color of the color frame buffer.
+ // The 2*n+1-th byte of texel 1 maps to a IA16 color where the low byte A (alpha) corresponds to
+ // the low byte of the n-th RGBA16 color of the color frame buffer.
+
+ // Since the combiner is in part set up to sum texel 0 color (I, intensity) with texel 1 alpha (A, alpha),
+ // the resulting color in the drawn rectangle is a desaturated color as defined by the `VISMONO_FAC_*` values.
+
+ gSPTextureRectangle(gfx++, 0, y << 2, SCREEN_WIDTH << 2, (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0, 2 << 10,
+ 1 << 10);
+ cfbFrag += SCREEN_WIDTH * height;
}
gDPPipeSync(gfx++);
gSPEndDisplayList(gfx++);
-#endif
return gfx;
}
-void VisMono_Draw(VisMono* this, Gfx** gfxp) {
- Gfx* gfx = *gfxp;
+void VisMono_Draw(VisMono* this, Gfx** gfxP) {
+ Gfx* gfx = *gfxP;
u16* tlut;
- Gfx* monoDL;
- Gfx* glistpEnd;
-
+ Gfx* dList;
+ Gfx* dListEnd;
+
+ // SOH [Port] Implement VisMono by performing a framebuffer copy and redraw with an active
+ // grayscale command to set the mono color
+ FB_CopyToFramebuffer(&gfx, 0, gReusableFrameBuffer, false, NULL);
+ gDPSetGrayscaleColor(gfx++, this->vis.primColor.r, this->vis.primColor.g, this->vis.primColor.b,
+ this->vis.primColor.a);
+ gSPGrayscale(gfx++, true);
+ FB_DrawFromFramebuffer(&gfx, gReusableFrameBuffer, 255);
+ gSPGrayscale(gfx++, false);
+
+#if 0
if (this->tlut) {
tlut = this->tlut;
} else {
- tlut = Graph_DlistAlloc(&gfx, 256 * sizeof(u16));
- VisMono_UpdateTexture(this, tlut);
+ tlut = Graph_DlistAlloc(&gfx, 256 * G_IM_SIZ_16b_BYTES);
+ VisMono_DesaturateTLUT(this, tlut);
}
- if (this->monoDl) {
- monoDL = this->monoDl;
+ if (this->dList) {
+ dList = this->dList;
} else {
- monoDL = Graph_DlistAlloc(&gfx, DLSIZE * sizeof(Gfx));
- glistpEnd = VisMono_DrawTexture(this, monoDL);
-
- if (!(glistpEnd <= monoDL + DLSIZE)) {
- LOG_ADDRESS("glistp_end", glistpEnd);
- LOG_ADDRESS("mono_dl", monoDL);
- LOG_ADDRESS("mono_dl + (1+3+1+1+80*(7+2+2+3)+1)", monoDL + DLSIZE);
- LOG_ADDRESS("(1+3+1+1+80*(7+2+2+3)+1)", DLSIZE);
+ dList = Graph_DlistAlloc(&gfx, VISMONO_DLSIZE * sizeof(Gfx));
+ dListEnd = VisMono_DesaturateDList(this, dList);
+
+ if (!(dListEnd <= dList + VISMONO_DLSIZE)) {
+ LOG_ADDRESS("glistp_end", dListEnd);
+ LOG_ADDRESS("mono_dl", dList);
+ LOG_ADDRESS("mono_dl + (1+3+1+1+80*(7+2+2+3)+1)", dList + VISMONO_DLSIZE);
+ LOG_ADDRESS("(1+3+1+1+80*(7+2+2+3)+1)", VISMONO_DLSIZE);
}
- assert(glistpEnd <= monoDL + DLSIZE);
+ ASSERT(dListEnd <= dList + VISMONO_DLSIZE, "glistp_end <= mono_dl + DLSIZE", "../z_vismono.c", 262);
}
gDPPipeSync(gfx++);
- if (this->setScissor == true) {
+
+ if (this->vis.scissorType == VIS_SETSCISSOR) {
gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}
- gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba);
- gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba);
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, this->vis.primColor.rgba);
+ gDPSetColor(gfx++, G_SETENVCOLOR, this->vis.envColor.rgba);
gDPLoadTLUT_pal256(gfx++, tlut);
- gSPDisplayList(gfx++, monoDL);
+ gSPDisplayList(gfx++, dList);
+
gDPPipeSync(gfx++);
+#endif
- *gfxp = gfx;
+ *gfxP = gfx;
}
void VisMono_DrawOld(VisMono* this) {
- Gfx* glistpEnd;
+ Gfx* dListEnd;
- if (!this->tlut) {
- this->tlut = SYSTEM_ARENA_MALLOC_DEBUG(256 * sizeof(u16));
- VisMono_UpdateTexture(this, this->tlut);
+ if (this->tlut == NULL) {
+ this->tlut = SYSTEM_ARENA_MALLOC(256 * G_IM_SIZ_16b_BYTES, "../z_vismono.c", 283);
+ VisMono_DesaturateTLUT(this, this->tlut);
}
- if (!this->monoDl) {
- this->monoDl = SYSTEM_ARENA_MALLOC_DEBUG(DLSIZE * sizeof(Gfx));
- glistpEnd = VisMono_DrawTexture(this, this->monoDl);
- assert(glistpEnd <= this->monoDl + DLSIZE);
+ if (this->dList == NULL) {
+ this->dList = SYSTEM_ARENA_MALLOC(VISMONO_DLSIZE * sizeof(Gfx), "../z_vismono.c", 289);
+ dListEnd = VisMono_DesaturateDList(this, this->dList);
+ ASSERT(dListEnd <= this->dList + VISMONO_DLSIZE, "glistp_end <= this->mono_dl + DLSIZE", "../z_vismono.c", 292);
}
}
diff --git a/soh/src/code/z_viszbuf.c b/soh/src/code/z_viszbuf.c
new file mode 100644
index 000000000..5a542bc12
--- /dev/null
+++ b/soh/src/code/z_viszbuf.c
@@ -0,0 +1,116 @@
+/**
+ * @file z_viszbuf.c
+ *
+ * This file implements a full-screen framebuffer effect for visualizing the z-buffer (AKA depth buffer), using either
+ * cycling RGBA or a single fading color.
+ *
+ * This is done by reading the z-buffer as if it were a color image, the format of which is specified by the selected
+ * vis type:
+ * - VIS_ZBUF_TYPE_IA : Produces a monotonic fade from primColor to envColor as depth increases.
+ * - VIS_ZBUF_TYPE_RGBA : Produces vibrant almost-periodic-looking bands.
+ *
+ * In both cases this occurs because of the format the depth information takes: it is 18-bit, and is a nonnegative
+ * floating-point number with
+ * bbb mmmmmmmmmmm dd|dd
+ * exponent mantissa dz value (only first 16 bits visible to CPU, the least significant 2 bits of dz are ignored)
+ *
+ * Reading z-buffer as IA16:
+ * bbbmmmmm mmmmmmdd
+ * iiiiiiii aaaaaaaa
+ *
+ * Since floating-point numbers of this format have the same ordering as their binary/hex representation, increasing
+ * the depth also increases the intensity in the IA16 representation and hence the interpolation parameter used to
+ * combine primColor and envColor. The alpha is ignored by the RenderMode.
+ *
+ * Reading z-buffer as RGBA16:
+ * bbbmm mmmmm mmmmd d
+ * rrrrr ggggg bbbbb a
+ *
+ * The red increases monotonically with the depth. The significant visible oscillation is the green component, because
+ * it rolls over every time the second-most-significant bit of the mantissa increments. The blue component oscillates
+ * too rapidly to be particularly visible (it rolls over when the 7th-most-significant bit increments). The alpha is
+ * again ignored by the RenderMode.
+ */
+
+#include "global.h"
+
+// Height of the fragments the z-buffer is split into.
+// It is the maximum amount of lines such that all rgba16 SCREEN_WIDTH-long lines fit into TMEM.
+#define VISZBUF_ZBUFFRAG_HEIGHT (TMEM_SIZE / (SCREEN_WIDTH * G_IM_SIZ_16b_BYTES))
+
+// z-buffer
+extern u16 D_0E000000[];
+
+/**
+ * Initialise to IA type with white and black as default colors.
+ */
+void VisZBuf_Init(VisZBuf* this) {
+ this->vis.type = VIS_ZBUF_TYPE_IA;
+ this->vis.scissorType = VIS_NO_SETSCISSOR;
+
+ this->vis.primColor.r = 255;
+ this->vis.primColor.g = 255;
+ this->vis.primColor.b = 255;
+ this->vis.primColor.a = 255;
+
+ // clang-format off
+ this->vis.envColor.r = 0; \
+ this->vis.envColor.g = 0; \
+ this->vis.envColor.b = 0; \
+ this->vis.envColor.a = 255;
+ // clang-format on
+}
+
+void VisZBuf_Destroy(VisZBuf* this) {
+}
+
+void VisZBuf_Draw(VisZBuf* this, Gfx** gfxP) {
+ Gfx* gfx = *gfxP;
+ s32 pad;
+ u16* zbufFrag = D_0E000000;
+ s32 fmt;
+ s32 y;
+ s32 height;
+
+ if (this->vis.type == VIS_ZBUF_TYPE_IA) {
+ fmt = G_IM_FMT_IA;
+ } else { // VIS_ZBUF_TYPE_RGBA
+ fmt = G_IM_FMT_RGBA;
+ }
+
+ height = VISZBUF_ZBUFFRAG_HEIGHT;
+
+ gDPPipeSync(gfx++);
+ // Scissoring is only required if the scissor has not been set prior.
+ if (this->vis.scissorType == VIS_SETSCISSOR) {
+ gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ }
+
+ // No palette so can use all of TMEM.
+ // G_RM_OPA_SURF discards all information previously in the pixel, and the current alpha, leaving only the color
+ // from this filter.
+ gDPSetOtherMode(gfx++,
+ G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE |
+ G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
+ G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2);
+
+ // LERP between primColor and envColor in 1-cycle mode using the z-buffer value.
+ gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT,
+ PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT);
+ gDPSetColor(gfx++, G_SETPRIMCOLOR, this->vis.primColor.rgba);
+ gDPSetColor(gfx++, G_SETENVCOLOR, this->vis.envColor.rgba);
+
+ for (y = 0; y <= SCREEN_HEIGHT - height; y += height) {
+ // Load a few lines of the z-buffer, as many as can fit in TMEM at once.
+ gDPLoadTextureBlock(gfx++, zbufFrag, fmt, G_IM_SIZ_16b, SCREEN_WIDTH, height, 0, G_TX_NOMIRROR | G_TX_CLAMP,
+ G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
+
+ // Overwrite them with the calculated colors.
+ gSPTextureRectangle(gfx++, 0, y << 2, SCREEN_WIDTH << 2, (y + height) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10,
+ 1 << 10);
+ zbufFrag += SCREEN_WIDTH * height;
+ }
+
+ gDPPipeSync(gfx++);
+ *gfxP = gfx;
+}