Files
mm/src/code/z_visfbuf.c
T
Anghelo Carvajal cd087d8511 Introduce a bunch of small new headers (#1649)
* regs.h

* gfxalloc.h

* z64inventory.h

* gfx.h

* graph.h

* z64olib.h

* z64elf_message.h

* fix missing include

* Include z64inventory.h on z64save.h

* fix bss

* Move PosRot to z64math.h

* So dumb

* Update src/overlays/actors/ovl_En_Kusa/z_en_kusa.c

Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>

* review

* Remove graph.h

* bss

---------

Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>
2024-07-28 13:53:28 -07:00

333 lines
13 KiB
C

/**
* @file z_visfbuf.c
* @brief Copies images between color images (generally framebuffers), possibly with filling, scaling, and colour
* interpolation.
*
* Used for several transition effects, and in z_play to shrink the screen at the end of the First and Second Days.
*
* @note to use the functions in this file, with the exception of VisFbuf_Draw(), it is necessary to load the S2DEX2
* microcode first, and then re-load the 3D microcode afterwards for the rest of the drawing in the frame; see
* VisFbuf_Draw() for how to do this.
*/
#include "z64visfbuf.h"
#include "gfxalloc.h"
#include "global.h"
#include "sys_cfb.h"
#include "sys_ucode.h"
#define SCALE_MIN 0.032f
#define SCALE_MAX 1.0f //!< also unchanged scale
void VisFbuf_Init(VisFbuf* this) {
bzero(this, sizeof(VisFbuf));
}
void VisFbuf_Destroy(VisFbuf* this) {
}
// internal, only used in VisFbuf_SetBg
/**
* Draw a bg to the specified color image.
*
* @param[in,out] gfxP Pointer to current displaylist
* @param[in] bg BG object to draw
* @param[out] img Pointer to beginning of destination color image
* @param[in] width Output width in pixels
* @param[in] height Output height in pixels
* @param[in] cycleMode Which BG mode to use
*/
void VisFbuf_DrawBgToColorImage(Gfx** gfxP, uObjBg* bg, void* img, s32 width, s32 height, VisFbufBgMode cycleMode) {
Gfx* gfx = *gfxP;
gDPPipeSync(gfx++);
// Set up color image to draw bg to
gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, img);
gDPSetScissor(gfx++, 0, 0, 0, width, height);
// Draw bg in appropriate type
gSPObjRenderMode(gfx++, G_OBJRM_ANTIALIAS | G_OBJRM_BILERP);
if (!!(cycleMode & VIS_FBUF_BG_CYC_COPY) != 0) { //! FAKE: may possibly be a better way
gSPBgRectCopy(gfx++, bg);
} else {
gSPBgRect1Cyc(gfx++, bg);
}
gDPPipeSync(gfx++);
// Reset the color image and scissor to frame's defaults
gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gCfbWidth, D_0F000000);
gSPDisplayList(gfx++, D_0E000000.setScissor);
*gfxP = gfx;
}
// internal
/**
* Set up a BG from a specified source image and draw it to the specified color image with
* VisFbuf_DrawBgToColorImage(), using the BG's settings. Use VisFbuf_SetBgSimple() or
* VisFbuf_SetBgGeneral()
*
* @param[out] gfxP Pointer to current displaylist
* @param[in] source Pointer to beginning of source color image
* @param[out] img Pointer to beginning of destination color image
* @param[in] width Output width in pixels
* @param[in] height Output height in pixels
* @param[in] x Left of drawn output
* @param[in] y Top of drawn output
* @param[in] scaleX Amount to rescale the image, (dsdx). No effect if `cycleMode` is `VIS_FBUF_BG_CYC_COPY`.
* @param[in] scaleY Amount to rescale the image, (dtdy). No effect if `cycleMode` is `VIS_FBUF_BG_CYC_COPY`.
* @param[in] cycleMode Which BG mode to use
*/
void VisFbuf_SetBg(Gfx** gfxP, void* source, void* img, s32 width, s32 height, f32 x, f32 y, f32 scaleX, f32 scaleY,
VisFbufBgMode cycleMode) {
Gfx* gfx = *gfxP;
Gfx* gfxTemp;
uObjBg* bg;
// Allocate for BG
gfxTemp = gfx;
bg = Gfx_Alloc(&gfxTemp, sizeof(uObjBg));
gfx = gfxTemp;
// Set up BG
bg->b.imageX = 0;
bg->b.imageW = width << 2;
bg->b.frameX = x * 4;
bg->b.frameW = width << 2;
bg->b.imageY = 0;
bg->b.imageH = height << 2;
bg->b.frameY = y * 4;
bg->b.frameH = height << 2;
bg->b.imageLoad = G_BGLT_LOADTILE;
bg->b.imageFmt = G_IM_FMT_RGBA;
bg->b.imageSiz = G_IM_SIZ_16b;
bg->b.imagePal = 0;
bg->b.imageFlip = 0;
bg->b.imagePtr = source;
if (!!(cycleMode & VIS_FBUF_BG_CYC_COPY) != 0) { //! FAKE: may possibly be a better way
guS2DInitBg(bg);
} else {
bg->s.scaleW = (s32)((1 << 10) / scaleX);
bg->s.scaleH = (s32)((1 << 10) / scaleY);
bg->s.imageYorig = bg->b.imageY;
}
// draw BG to `img`
VisFbuf_DrawBgToColorImage(&gfx, bg, img, width, height, cycleMode);
*gfxP = gfx;
}
// used in FbdemoWipe5 and internally
// "default settings" wrapper for VisFbuf_SetBg
/**
* Set up a BG from a specified source image and draw it to the specified color image with
* VisFbuf_DrawBgToColorImage(), using the BG's settings. Position uses the default (0,0), and no rescaling is done
* even if 1-cycle mode is enabled.
*
* @see VisFbuf_SetBg() for arguments.
*/
void VisFbuf_SetBgSimple(Gfx** gfxP, void* source, void* img, s32 width, s32 height, VisFbufBgMode cycleMode) {
VisFbuf_SetBg(gfxP, source, img, width, height, 0.0f, 0.0f, SCALE_MAX, SCALE_MAX, cycleMode);
}
// wrapper for VisFbuf_SetBg with general arguments, used in VisFbuf_ApplyEffects
/**
* Set up a BG from a specified source image and draw it to the specified color image with
* VisFbuf_DrawBgToColorImage(). Fully general settings are available.
*
* @see VisFbuf_SetBg() for arguments.
*/
void VisFbuf_SetBgGeneral(Gfx** gfxP, void* source, void* img, s32 width, s32 height, f32 x, f32 y, f32 scaleX,
f32 scaleY, VisFbufBgMode cycleMode) {
VisFbuf_SetBg(gfxP, source, img, width, height, x, y, scaleX, scaleY, cycleMode);
}
// internal, used in VisFbuf_DrawGeneral
/**
* Most general redrawing function.
*
* - Copies `source` to `img`,
* - fills the current framebuffer with `this->primColor`,
* - applies colour interpolation to `img`,
* - copies `img` back to `source`, with rescaling.
*
* @param[in] this
* @param[in,out] gfxP Pointer to current displaylist
* @param[in,out] source Pointer to beginning of in/out color image
* @param[out] img Pointer to beginning of destination color image
* @param[in] width Output width in pixels
* @param[in] height Output height in pixels
*/
void VisFbuf_ApplyEffects(VisFbuf* this, Gfx** gfxP, void* source, void* img, s32 width, s32 height) {
Gfx* gfx = *gfxP;
s32 pad[3];
gDPPipeSync(gfx++);
// copy source to img
gDPSetOtherMode(gfx++,
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_COPY | G_PM_NPRIMITIVE,
G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2);
VisFbuf_SetBgSimple(&gfx, source, img, width, height, VIS_FBUF_BG_CYC_COPY);
gDPPipeSync(gfx++);
// fill framebuffer with primColor
gDPSetOtherMode(gfx++,
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_FILL | G_PM_NPRIMITIVE,
G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2);
{
s32 color = GPACK_RGBA5551(this->primColor.r, this->primColor.g, (u32)this->primColor.b, 1);
gDPSetFillColor(gfx++, (color << 0x10) | color);
}
//! @bug VisFbuf_SetBgSimple() sets the current color image back to the frame's default framebuffer at the end,
//! so this will always fill in the default framebuffer, whatever are used as `source` and `img`. This does not
//! arise in-game since this function is always used with `source = D_0F000000`.
gDPFillRectangle(gfx++, 0, 0, width - 1, height - 1);
gDPPipeSync(gfx++);
// Set lod and primColor from struct, perform interpolation, draw image with scaling (this is the most general
// alteration this system can carry out).
{
s32 lodFrac = this->lodProportion * 255;
gDPSetLODColor(gfx++, G_SETPRIMCOLOR, 0, lodFrac, this->primColor.rgba);
}
if (this->envColor.a == 0) {
// Interpolate between primColor and texel color using lod (but with max alpha)
gDPSetOtherMode(gfx++,
G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE |
G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE,
G_AC_THRESHOLD | G_ZS_PIXEL | AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | ALPHA_CVG_SEL |
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_BL, G_BL_1MA) |
GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_BL, G_BL_1MA));
gDPSetCombineLERP(gfx++, PRIMITIVE, TEXEL0, PRIM_LOD_FRAC, TEXEL0, 0, 0, 0, 1, PRIMITIVE, TEXEL0, PRIM_LOD_FRAC,
TEXEL0, 0, 0, 0, 1);
} else {
// Interpolate between envColor (the new black) and primColor (the new white)
gDPSetOtherMode(gfx++,
G_AD_DISABLE | G_CD_DISABLE | 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_PIXEL | G_RM_CLD_SURF | G_RM_NOOP2);
gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba);
gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0,
ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0,
ENVIRONMENT);
}
// Draw scaled image in centre of `width * height` rectangle in `source`
{
f32 scale = CLAMP_ALT(this->scale, SCALE_MIN, SCALE_MAX);
VisFbuf_SetBgGeneral(&gfx, img, source, width, height, width * 0.5f * (1.0f - scale),
height * 0.5f * (1.0f - scale), scale, scale, VIS_FBUF_BG_CYC_1CYC);
}
gDPPipeSync(gfx++);
*gfxP = gfx;
}
// internal, used in VisFbuf_Draw, VIS_FBUF_MODE_GENERAL
/**
* If scale is within `(SCALE_MIN, SCALE_MAX)`, apply VisFbuf_ApplyEffects().
* If it is smaller than `SCALE_MIN`, fill the framebuffer with `this->primColor`
*
* @param[in] this
* @param[in,out] gfxP Pointer to current displaylist
* @param[in,out] source Pointer to beginning of source color image
* @param[in,out] img Pointer to beginning of destination color image
* @param[in] width Output width in pixels
* @param[in] height Output height in pixels
*/
void VisFbuf_DrawGeneral(VisFbuf* this, Gfx** gfxP, void* source, void* img, s32 width, s32 height) {
if (this->scale < SCALE_MAX) {
Gfx* gfx = *gfxP;
u32 color;
if (this->scale > SCALE_MIN) {
VisFbuf_ApplyEffects(this, &gfx, source, img, width, height);
} else {
gDPPipeSync(gfx++);
// fill the framebuffer with this->primColor
gDPSetOtherMode(gfx++,
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_FILL | G_PM_NPRIMITIVE,
G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2);
color = GPACK_RGBA5551(this->primColor.r, this->primColor.g, (u32)this->primColor.b, 1);
gDPSetFillColor(gfx++, (color << 0x10) | color);
//! @bug See corresponding note in VisFbuf_ApplyEffects()
gDPFillRectangle(gfx++, 0, 0, width - 1, height - 1);
gDPPipeSync(gfx++);
}
*gfxP = gfx;
}
}
/**
* Redraw `img` in-place, using texel color to interpolate between envColor (the new black) and primColor (the new
* white)
*
* @param[in] this
* @param[in,out] gfxP Pointer to current displaylist
* @param[in,out] img Pointer to beginning of color image to modify in-place
* @param[in] width width of img
* @param[in] height height of img
*/
void VisFbuf_DrawInterpolate(VisFbuf* this, Gfx** gfxP, void* img, s32 width, s32 height) {
Gfx* gfx = *gfxP;
gDPPipeSync(gfx++);
gDPSetOtherMode(gfx++,
G_AD_DISABLE | G_CD_DISABLE | 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_PIXEL | G_RM_CLD_SURF | G_RM_NOOP2);
gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba);
gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba);
gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT,
PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT);
VisFbuf_SetBgSimple(&gfx, img, img, width, height, VIS_FBUF_BG_CYC_1CYC);
gDPPipeSync(gfx++);
*gfxP = gfx;
}
// used in Play and FbdemoWipe4
/**
* main drawing function in this file, draws depending on settings specified in `this`.
*
* @param[in] this
* @param[in,out] gfxP Pointer to current displaylist
* @param[in,out] img Pointer to beginning of destination color image
*/
void VisFbuf_Draw(VisFbuf* this, Gfx** gfxP, void* img) {
Gfx* gfx = *gfxP;
gSPLoadUcodeL(gfx++, gspS2DEX2_fifo);
switch (this->mode) {
case VIS_FBUF_MODE_GENERAL:
VisFbuf_DrawGeneral(this, &gfx, D_0F000000, img, gScreenWidth, gScreenHeight);
break;
case VIS_FBUF_MODE_INTERPOLATE:
VisFbuf_DrawInterpolate(this, &gfx, D_0F000000, gScreenWidth, gScreenHeight);
break;
default:
break;
}
gSPLoadUcode(gfx++, SysUcode_GetUCode(), SysUcode_GetUCodeData());
*gfxP = gfx;
}