diff --git a/include/regs.h b/include/regs.h index 8d67563d29e..89f6a03267d 100644 --- a/include/regs.h +++ b/include/regs.h @@ -68,9 +68,9 @@ struct PlayState; #define R_VI_MODE_EDIT_ULX_ADJ SREG(53) #define R_VI_MODE_EDIT_LRX_ADJ SREG(54) #define R_FB_FILTER_TYPE SREG(80) -#define R_FB_FILTER_PRIM_COLOR(c) SREG(81 + (c)) +#define R_FB_FILTER_COLOR1(c) SREG(81 + (c)) #define R_FB_FILTER_A SREG(84) -#define R_FB_FILTER_ENV_COLOR(c) SREG(85 + (c)) +#define R_FB_FILTER_COLOR2(c) SREG(85 + (c)) #define R_ENABLE_FB_FILTER SREG(88) #define R_PAUSE_BG_PRERENDER_STATE SREG(94) // `PauseBgPreRenderState` #define R_CAM_XZ_OFFSET_UPDATE_RATE OREG(2) diff --git a/include/vis.h b/include/vis.h index 52b1f0a690c..3aa3691eca5 100644 --- a/include/vis.h +++ b/include/vis.h @@ -4,81 +4,51 @@ #include "ultra64.h" #include "color.h" -typedef enum FramebufferFilterType { - /* 0 */ FB_FILTER_NONE, - /* 1 */ FB_FILTER_CVG_RGB, - /* 2 */ FB_FILTER_CVG_RGB_UNIFORM, - /* 3 */ FB_FILTER_CVG_ONLY, - /* 4 */ FB_FILTER_CVG_RGB_FOG, // Not recommended, easily overflows blender - /* 5 */ FB_FILTER_ZBUF_IA, - /* 6 */ FB_FILTER_ZBUF_RGBA, - /* 7 */ FB_FILTER_MONO -} FramebufferFilterType; - -typedef enum VisScissorType { - /* 0 */ VIS_NO_SETSCISSOR, - /* 1 */ VIS_SETSCISSOR -} VisScissorType; - -typedef struct Vis { +/** + * Meaning of color1 and color2: + * - VisCvg: see VisCvgType for color1. color2 unused + * - VisMono: LERP endpoints. See z_vismono.c file comment + * - VisZBuffer: LERP endpoints. See z_viszbuffer.c file comment + */ +typedef struct VisParams { /* 0x00 */ u32 type; - /* 0x04 */ u32 scissorType; - /* 0x08 */ Color_RGBA8_u32 primColor; - /* 0x0C */ Color_RGBA8_u32 envColor; -} Vis; // size = 0x10 - - -/* Cvg: Coverage */ - -#define FB_FILTER_TO_CVG_TYPE(filter) (filter) + /* 0x04 */ u32 setScissor; + /* 0x08 */ Color_RGBA8_u32 color1; + /* 0x0C */ Color_RGBA8_u32 color2; +} VisParams; // size = 0x10 typedef enum VisCvgType { - /* 0 */ VIS_CVG_TYPE_NONE = FB_FILTER_TO_CVG_TYPE(FB_FILTER_NONE), - /* 1 */ VIS_CVG_TYPE_CVG_RGB = FB_FILTER_TO_CVG_TYPE(FB_FILTER_CVG_RGB), - /* 2 */ VIS_CVG_TYPE_CVG_RGB_UNIFORM = FB_FILTER_TO_CVG_TYPE(FB_FILTER_CVG_RGB_UNIFORM), - /* 3 */ VIS_CVG_TYPE_CVG_ONLY = FB_FILTER_TO_CVG_TYPE(FB_FILTER_CVG_ONLY), - /* 4 */ VIS_CVG_TYPE_CVG_RGB_FOG = FB_FILTER_TO_CVG_TYPE(FB_FILTER_CVG_RGB_FOG) + VISCVG_TYPE_NOP, + VISCVG_TYPE_MODULATE_FB, // Multiply coverage with framebuffer + VISCVG_TYPE_FADE_AND_MODULATE_FB, // Also fade to color1 by color1.a + VISCVG_TYPE_MODULATE_COLOR, // Multiply coverage with color1 + VISCVG_TYPE_MODULATE_FB_ADDITIVE_COLOR // Add color1 } VisCvgType; typedef struct VisCvg { - /* 0x00 */ Vis vis; + /* 0x00 */ VisParams params; // params.color2 unused } VisCvg; // size = 0x10 -void VisCvg_Init(VisCvg* this); -void VisCvg_Destroy(VisCvg* this); -void VisCvg_Draw(VisCvg* this, Gfx** gfxP); - - -/* Mono: Desaturation */ - -// Only one type - typedef struct VisMono { - /* 0x00 */ Vis vis; + /* 0x00 */ VisParams params; // params.type unused /* 0x10 */ u16* tlut; /* 0x14 */ Gfx* dList; } VisMono; // size = 0x18 -void VisMono_Init(VisMono* this); -void VisMono_Destroy(VisMono* this); -void VisMono_Draw(VisMono* this, Gfx** gfxP); - - -/* ZBuf: Z-Buffer */ +typedef struct VisZBuffer { + /* 0x00 */ VisParams params; +} VisZBuffer; // size = 0x10 -#define FB_FILTER_TO_ZBUF_TYPE(filter) ((filter) - FB_FILTER_ZBUF_IA) - -typedef enum VisZBufType { - /* 0 */ VIS_ZBUF_TYPE_IA = FB_FILTER_TO_ZBUF_TYPE(FB_FILTER_ZBUF_IA), - /* 1 */ VIS_ZBUF_TYPE_RGBA = FB_FILTER_TO_ZBUF_TYPE(FB_FILTER_ZBUF_RGBA) -} VisZBufType; +void VisCvg_Init(VisCvg* this); +void VisCvg_Destroy(VisCvg* this); +void VisCvg_Draw(VisCvg* this, Gfx** gfxp); -typedef struct VisZBuf { - /* 0x00 */ Vis vis; -} VisZBuf; // size = 0x10 +void VisMono_Init(VisMono* this); +void VisMono_Destroy(VisMono* this); +void VisMono_Draw(VisMono* this, Gfx** gfxp); -void VisZBuf_Init(VisZBuf* this); -void VisZBuf_Destroy(VisZBuf* this); -void VisZBuf_Draw(VisZBuf* this, Gfx** gfxP); +void VisZBuffer_Init(VisZBuffer* this); +void VisZBuffer_Destroy(VisZBuffer* this); +void VisZBuffer_Draw(VisZBuffer* this, Gfx** gfxp); #endif diff --git a/spec/spec b/spec/spec index 76615133bec..5ba1e15dad4 100644 --- a/spec/spec +++ b/spec/spec @@ -594,7 +594,7 @@ beginseg include "$(BUILD_DIR)/src/code/z_vimode.o" include "$(BUILD_DIR)/src/code/z_viscvg.o" include "$(BUILD_DIR)/src/code/z_vismono.o" - include "$(BUILD_DIR)/src/code/z_viszbuf.o" + include "$(BUILD_DIR)/src/code/z_viszbuffer.o" include "$(BUILD_DIR)/src/code/z_vr_box.o" include "$(BUILD_DIR)/src/code/z_vr_box_draw.o" include "$(BUILD_DIR)/src/code/z_player_call.o" diff --git a/src/code/game.c b/src/code/game.c index 1636633f97d..0ba65b3edf7 100644 --- a/src/code/game.c +++ b/src/code/game.c @@ -35,9 +35,9 @@ #pragma increment_block_number "gc-eu:0 gc-eu-mq:0 gc-jp:0 gc-jp-ce:0 gc-jp-mq:0 gc-us:0 gc-us-mq:0" SpeedMeter D_801664D0; -VisCvg sVisCvg; -VisZBuf sVisZBuf; -VisMono sVisMono; +VisCvg sGameStateVisCvg; +VisZBuffer sGameStateVisZBuffer; +VisMono sGameStateVisMono; ViMode sViMode; #if DEBUG_FEATURES @@ -58,43 +58,40 @@ void GameState_FaultPrint(void) { } #endif -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); +void GameState_SetFBFilter(Gfx** gfx) { + Gfx* gfxP = *gfx; + + if ((R_FB_FILTER_TYPE > 0) && (R_FB_FILTER_TYPE < 5)) { + sGameStateVisCvg.params.type = R_FB_FILTER_TYPE; + sGameStateVisCvg.params.color1.r = R_FB_FILTER_COLOR1(0); + sGameStateVisCvg.params.color1.g = R_FB_FILTER_COLOR1(1); + sGameStateVisCvg.params.color1.b = R_FB_FILTER_COLOR1(2); + sGameStateVisCvg.params.color1.a = R_FB_FILTER_A; + VisCvg_Draw(&sGameStateVisCvg, &gfxP); + } else if ((R_FB_FILTER_TYPE == 5) || (R_FB_FILTER_TYPE == 6)) { + sGameStateVisZBuffer.params.type = (R_FB_FILTER_TYPE == 6); + sGameStateVisZBuffer.params.color1.r = R_FB_FILTER_COLOR1(0); + sGameStateVisZBuffer.params.color1.g = R_FB_FILTER_COLOR1(1); + sGameStateVisZBuffer.params.color1.b = R_FB_FILTER_COLOR1(2); + sGameStateVisZBuffer.params.color1.a = R_FB_FILTER_A; + sGameStateVisZBuffer.params.color2.r = R_FB_FILTER_COLOR2(0); + sGameStateVisZBuffer.params.color2.g = R_FB_FILTER_COLOR2(1); + sGameStateVisZBuffer.params.color2.b = R_FB_FILTER_COLOR2(2); + sGameStateVisZBuffer.params.color2.a = R_FB_FILTER_A; + VisZBuffer_Draw(&sGameStateVisZBuffer, &gfxP); + } else if (R_FB_FILTER_TYPE == 7) { + sGameStateVisMono.params.type = 0; + sGameStateVisMono.params.color1.r = R_FB_FILTER_COLOR1(0); + sGameStateVisMono.params.color1.g = R_FB_FILTER_COLOR1(1); + sGameStateVisMono.params.color1.b = R_FB_FILTER_COLOR1(2); + sGameStateVisMono.params.color1.a = R_FB_FILTER_A; + sGameStateVisMono.params.color2.r = R_FB_FILTER_COLOR2(0); + sGameStateVisMono.params.color2.g = R_FB_FILTER_COLOR2(1); + sGameStateVisMono.params.color2.b = R_FB_FILTER_COLOR2(2); + sGameStateVisMono.params.color2.a = R_FB_FILTER_A; + VisMono_Draw(&sGameStateVisMono, &gfxP); } - *gfxP = gfx; + *gfx = gfxP; } void func_800C4344(GameState* gameState) { @@ -498,9 +495,9 @@ void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* g startTime = endTime; LOG_UTILS_CHECK_NULL_POINTER("this->cleanup", gameState->destroy, "../game.c", 1088); - VisCvg_Init(&sVisCvg); - VisZBuf_Init(&sVisZBuf); - VisMono_Init(&sVisMono); + VisCvg_Init(&sGameStateVisCvg); + VisZBuffer_Init(&sGameStateVisZBuffer); + VisMono_Init(&sGameStateVisMono); if ((R_VI_MODE_EDIT_STATE == VI_MODE_EDIT_STATE_INACTIVE) || !DEBUG_FEATURES) { ViMode_Init(&sViMode); } @@ -529,9 +526,9 @@ void GameState_Destroy(GameState* gameState) { } Rumble_Destroy(); SpeedMeter_Destroy(&D_801664D0); - VisCvg_Destroy(&sVisCvg); - VisZBuf_Destroy(&sVisZBuf); - VisMono_Destroy(&sVisMono); + VisCvg_Destroy(&sGameStateVisCvg); + VisZBuffer_Destroy(&sGameStateVisZBuffer); + VisMono_Destroy(&sGameStateVisMono); if ((R_VI_MODE_EDIT_STATE == VI_MODE_EDIT_STATE_INACTIVE) || !DEBUG_FEATURES) { ViMode_Destroy(&sViMode); } diff --git a/src/code/z_play.c b/src/code/z_play.c index 8d73e9e5658..7ac82e385fc 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -47,12 +47,12 @@ #include "save.h" #include "vis.h" -#pragma increment_block_number "gc-eu:224 gc-eu-mq:224 gc-jp:224 gc-jp-ce:224 gc-jp-mq:224 gc-us:224 gc-us-mq:224" \ - "ique-cn:224 ntsc-1.0:240 ntsc-1.1:240 ntsc-1.2:240 pal-1.0:240 pal-1.1:240" +#pragma increment_block_number "gc-eu:221 gc-eu-mq:221 gc-jp:221 gc-jp-ce:221 gc-jp-mq:221 gc-us:221 gc-us-mq:221" \ + "ique-cn:221 ntsc-1.0:221 ntsc-1.1:221 ntsc-1.2:221 pal-1.0:221 pal-1.1:221" TransitionTile gTransitionTile; s32 gTransitionTileState; -VisMono gPlayVisMono; +VisMono sPlayVisMono; Color_RGBA8_u32 gVisMonoColor; #if DEBUG_FEATURES @@ -261,7 +261,7 @@ void Play_Destroy(GameState* thisx) { Letterbox_Destroy(); TransitionFade_Destroy(&this->transitionFadeFlash); - VisMono_Destroy(&gPlayVisMono); + VisMono_Destroy(&sPlayVisMono); if (gSaveContext.save.linkAge != this->linkAgeOnLoad) { Inventory_SwapAgeEquipment(); @@ -482,7 +482,7 @@ void Play_Init(GameState* thisx) { TransitionFade_SetType(&this->transitionFadeFlash, TRANS_INSTANCE_TYPE_FADE_FLASH); TransitionFade_SetColor(&this->transitionFadeFlash, RGBA8(160, 160, 160, 255)); TransitionFade_Start(&this->transitionFadeFlash); - VisMono_Init(&gPlayVisMono); + VisMono_Init(&sPlayVisMono); gVisMonoColor.a = 0; CutsceneFlags_UnsetAll(this); @@ -1217,8 +1217,8 @@ void Play_Draw(PlayState* this) { if (gVisMonoColor.a > 0) #endif { - gPlayVisMono.vis.primColor.rgba = gVisMonoColor.rgba; - VisMono_Draw(&gPlayVisMono, &gfxP); + sPlayVisMono.params.color1.rgba = gVisMonoColor.rgba; + VisMono_Draw(&sPlayVisMono, &gfxP); } gSPEndDisplayList(gfxP++); diff --git a/src/code/z_viscvg.c b/src/code/z_viscvg.c index 1616b661c04..f86417c90b2 100644 --- a/src/code/z_viscvg.c +++ b/src/code/z_viscvg.c @@ -1,146 +1,108 @@ /** - * @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. + * This file implements framebuffer effects relying on coverage. + * Coverage's primary use is performing antialiasing: it basically tracks how much of a pixel is covered by its current + * color. Coverage is stored in a rgba16 framebuffer as a 3 bits value. + * Coverage is typically partial on geometry outer edges and full elsewhere: this makes the framebuffer effects here + * usually highlight silhouettes. */ +#include "ultra64.h" #include "gfx.h" #include "vis.h" -/** - * Draws only coverage: does not retain any of the original pixel RGB, primColor is used as background color. - */ -Gfx sCoverageOnlyDL[] = { +Gfx sVisCvgModulateBlendColorDL[] = { 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[] = { +Gfx sVisCvgModulateFramebufferAdditiveFogDL[] = { 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 | + // Note: additive blending often overflows the blender, causing visual bugs 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[] = { +Gfx sVisCvgModulateFramebufferDL[] = { 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[] = { +Gfx sVisCvgFadeToPrimAndModulateFramebuffer[] = { + // Fade the framebuffer towards prim color 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, + // Note: G_RM_CLD_SURF sets CVG_DST_SAVE which preserves coverage 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), - + // Like sVisCvgModulateFramebufferDL 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; + this->params.type = VISCVG_TYPE_NOP; + this->params.setScissor = false; + this->params.color1.r = 255; + this->params.color1.g = 255; + this->params.color1.b = 255; + this->params.color1.a = 255; } void VisCvg_Destroy(VisCvg* this) { } -void VisCvg_Draw(VisCvg* this, Gfx** gfxP) { - Gfx* gfx = *gfxP; +void VisCvg_Draw(VisCvg* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; gDPPipeSync(gfx++); + // Set full dz, see comment in PreRender_FetchFbufCoverage for details. gDPSetPrimDepth(gfx++, 0xFFFF, 0xFFFF); - if (this->vis.scissorType == VIS_SETSCISSOR) { + if (this->params.setScissor == true) { 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); + switch (this->params.type) { + case VISCVG_TYPE_MODULATE_FB: + gSPDisplayList(gfx++, sVisCvgModulateFramebufferDL); break; - case FB_FILTER_CVG_ONLY: - // Set background color for G_RM_VISCVG - gDPSetColor(gfx++, G_SETBLENDCOLOR, this->vis.primColor.rgba); - gSPDisplayList(gfx++, sCoverageOnlyDL); + case VISCVG_TYPE_FADE_AND_MODULATE_FB: + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->params.color1.rgba); + gSPDisplayList(gfx++, sVisCvgFadeToPrimAndModulateFramebuffer); 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); + case VISCVG_TYPE_MODULATE_COLOR: + gDPSetColor(gfx++, G_SETBLENDCOLOR, this->params.color1.rgba); + gSPDisplayList(gfx++, sVisCvgModulateBlendColorDL); break; - default: + case VISCVG_TYPE_MODULATE_FB_ADDITIVE_COLOR: + gDPSetColor(gfx++, G_SETFOGCOLOR, this->params.color1.rgba); + gSPDisplayList(gfx++, sVisCvgModulateFramebufferAdditiveFogDL); break; } gDPPipeSync(gfx++); - *gfxP = gfx; + *gfxp = gfx; } diff --git a/src/code/z_vismono.c b/src/code/z_vismono.c index 632d00a6e7d..ffe1fe7111b 100644 --- a/src/code/z_vismono.c +++ b/src/code/z_vismono.c @@ -1,11 +1,14 @@ /** - * @file z_vismono.c + * Color frame buffer effect to desaturate the colors. * - * This file implements a full-screen framebuffer effect for desaturating the contents of the framebuffer image. + * The result of the desaturation is used to LERP from color2 (0) to color1 (1). + * In the simplest case, to get a grayscale result, set color1 to white and color2 to black. * - * 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. + * This effect is achieved by making the RDP read the rgba16 framebuffer as a ci8 texture using a specific ia16 palette. + * For every two-bytes pixel in the rgba16 framebuffer, the palette maps in particular an intensity to the high byte and + * an alpha value to the low byte. The sum of those intensity and alpha value corresponds to the gray level of the + * desaturated color. This sum is done by the RDP with a clever setup of texture tiles and an adequate combiner. + * See the rest of the file for specifics. */ #include "libc64/malloc.h" @@ -17,7 +20,7 @@ // 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. +// 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`. @@ -37,16 +40,16 @@ extern u16 D_0F000000[]; void VisMono_Init(VisMono* this) { bzero(this, sizeof(VisMono)); - 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; + this->params.type = 0; + this->params.setScissor = false; + this->params.color1.r = 255; + this->params.color1.g = 255; + this->params.color1.b = 255; + this->params.color1.a = 255; + this->params.color2.r = 0; + this->params.color2.g = 0; + this->params.color2.b = 0; + this->params.color2.a = 0; } void VisMono_Destroy(VisMono* this) { @@ -105,8 +108,7 @@ Gfx* VisMono_DesaturateDList(VisMono* this, Gfx* gfx) { // 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. + // (palette is set to 1 here but that's ignored in the case of CI8 where there is only one palette) 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, (height - 1) << 2); @@ -141,8 +143,8 @@ Gfx* VisMono_DesaturateDList(VisMono* this, Gfx* gfx) { 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* dList; Gfx* dListEnd; @@ -171,12 +173,12 @@ void VisMono_Draw(VisMono* this, Gfx** gfxP) { gDPPipeSync(gfx++); - if (this->vis.scissorType == VIS_SETSCISSOR) { + if (this->params.setScissor == true) { gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); } - gDPSetColor(gfx++, G_SETPRIMCOLOR, this->vis.primColor.rgba); - gDPSetColor(gfx++, G_SETENVCOLOR, this->vis.envColor.rgba); + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->params.color1.rgba); + gDPSetColor(gfx++, G_SETENVCOLOR, this->params.color2.rgba); gDPLoadTLUT_pal256(gfx++, tlut); @@ -184,7 +186,7 @@ void VisMono_Draw(VisMono* this, Gfx** gfxP) { gDPPipeSync(gfx++); - *gfxP = gfx; + *gfxp = gfx; } void VisMono_DrawOld(VisMono* this) { diff --git a/src/code/z_viszbuf.c b/src/code/z_viszbuf.c deleted file mode 100644 index aa722897420..00000000000 --- a/src/code/z_viszbuf.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @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 "gfx.h" -#include "vis.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; -} diff --git a/src/code/z_viszbuffer.c b/src/code/z_viszbuffer.c new file mode 100644 index 00000000000..e19f44f3845 --- /dev/null +++ b/src/code/z_viszbuffer.c @@ -0,0 +1,100 @@ +/** + * This file implements a framebuffer effect relying on the depth buffer (Z buffer). + * It merely allows LERPing between two colors (from color2 to color1), using the Z buffer bytes (interpreted as either + * ia16 or rgba16 depending on the type being 0 or not) as the factor. + * + * Z buffer values are two bytes each. They are floating point, which makes the effect not that interesting for an + * actual application as that doesn't map well to be interpreted as ia16 or rgba16, though some discontinuous gradients + * can be seen. + * + * The bit layout of Z buffer values is: + * 0bEEEM_MMMM_MMMM_MMDD + * Where E is the 3 exponent bits, M is the 11 mantissa bits, + * and D is 2 of the 4 dz value bits (the remaining two are stored in the "hidden" 9th bit of each byte). + * In 3D space, Z values increase from front (near plane) to back (far plane). + * + * Interpreted as ia16: + * 0bIIII_IIII_AAAA_AAAA + * The intensity channel takes its value from the exponent bits and the high mantissa bits, so it continuously increases + * from front to back. + * The alpha channel mostly takes its value from the lowest mantissa bits, so it increases from front to back but also + * rolls over frequently. + * This can be observed in-game by setting color1 to white and color2 to black. + * From front to back, the intensity gradient is a continuous increase. + * The alpha is ignored due to the render mode VisZBuffer uses. + * + * Interpreted as rgba16: + * 0bRRRR_RGGG_GGBB_BBBA + * The red channel mostly takes its value from the exponent bits, so it continuously increases from front to back. + * The green channel takes its value from the middle mantissa bits, so it increases from front to back but also rolls + * over frequently. + * The blue channel mostly takes its value from the lowest mantissa bits, so it increases from front to back and also + * rolls over even more frequently. + * This can be observed in-game by setting color1 to white and color2 to black. + * From front to back, the gradients for each color channel are then: + * - red: continuous increase + * - green: discontinuous succession of continuous increases + * - blue: like green but even higher frequency + */ + +#include "ultra64.h" +#include "gfx.h" +#include "vis.h" + +// z-buffer +extern u16 D_0E000000[]; + +void VisZBuffer_Init(VisZBuffer* this) { + this->params.type = 0; + this->params.setScissor = false; + this->params.color1.r = 255; + this->params.color1.g = 255; + this->params.color1.b = 255; + this->params.color1.a = 255; + + // clang-format off + this->params.color2.r = 0; \ + this->params.color2.g = 0; \ + this->params.color2.b = 0; \ + this->params.color2.a = 255; + // clang-format on +} + +void VisZBuffer_Destroy(VisZBuffer* this) { +} + +void VisZBuffer_Draw(VisZBuffer* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; + s32 pad; + u16* tex = D_0E000000; + s32 fmt = this->params.type == 0 ? G_IM_FMT_IA : G_IM_FMT_RGBA; + s32 y; + s32 height = TMEM_SIZE / (SCREEN_WIDTH * G_IM_SIZ_16b_BYTES); + + gDPPipeSync(gfx++); + if (this->params.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->params.color1.rgba); + gDPSetColor(gfx++, G_SETENVCOLOR, this->params.color2.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; +} diff --git a/undefined_syms.txt b/undefined_syms.txt index c40597854c0..15e395b2401 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -1,7 +1,7 @@ // z_kankyo, z_demo_kankyo, z_en_viewer, z_object_kankyo, z_eff_ss_dead_dd D_01000000 = 0x01000000; -// z_viszbuf +// z_viszbuffer D_0E000000 = 0x0E000000; // z_vismono