Files
Thomas Ryan fb156d78ca Fix GX indirect tile window rendering (#218)
Co-authored-by: Thomas Ryan <contact@thomasryan.ca>
2026-06-02 10:04:31 -06:00

199 lines
7.0 KiB
C++

#include "gx.hpp"
#include "__gx.h"
extern "C" {
void GXSetNumIndStages(u8 num) {
SET_REG_FIELD(0, __gx->genMode, 3, 16, num);
__gx->dirtyState |= 6; // gen mode dirty + BP mask dirty
}
void GXSetIndTexOrder(GXIndTexStageID indStage, GXTexCoordID texCoord, GXTexMapID texMap) {
switch (indStage) {
case GX_INDTEXSTAGE0:
SET_REG_FIELD(0, __gx->iref, 3, 0, texMap);
SET_REG_FIELD(0, __gx->iref, 3, 3, texCoord);
break;
case GX_INDTEXSTAGE1:
SET_REG_FIELD(0, __gx->iref, 3, 6, texMap);
SET_REG_FIELD(0, __gx->iref, 3, 9, texCoord);
break;
case GX_INDTEXSTAGE2:
SET_REG_FIELD(0, __gx->iref, 3, 12, texMap);
SET_REG_FIELD(0, __gx->iref, 3, 15, texCoord);
break;
case GX_INDTEXSTAGE3:
SET_REG_FIELD(0, __gx->iref, 3, 18, texMap);
SET_REG_FIELD(0, __gx->iref, 3, 21, texCoord);
break;
}
GX_WRITE_RAS_REG(__gx->iref);
__gx->bpSent = 1;
__gx->dirtyState |= 3; // SU tex regs + BP mask dirty
}
void GXSetIndTexCoordScale(GXIndTexStageID indStage, GXIndTexScale scaleS, GXIndTexScale scaleT) {
if (indStage < GX_INDTEXSTAGE2) {
switch (indStage) {
case GX_INDTEXSTAGE0:
SET_REG_FIELD(0, __gx->IndTexScale0, 4, 0, scaleS);
SET_REG_FIELD(0, __gx->IndTexScale0, 4, 4, scaleT);
break;
case GX_INDTEXSTAGE1:
SET_REG_FIELD(0, __gx->IndTexScale0, 4, 8, scaleS);
SET_REG_FIELD(0, __gx->IndTexScale0, 4, 12, scaleT);
break;
}
GX_WRITE_RAS_REG(__gx->IndTexScale0);
} else {
switch (indStage) {
case GX_INDTEXSTAGE2:
SET_REG_FIELD(0, __gx->IndTexScale1, 4, 0, scaleS);
SET_REG_FIELD(0, __gx->IndTexScale1, 4, 4, scaleT);
break;
case GX_INDTEXSTAGE3:
SET_REG_FIELD(0, __gx->IndTexScale1, 4, 8, scaleS);
SET_REG_FIELD(0, __gx->IndTexScale1, 4, 12, scaleT);
break;
}
GX_WRITE_RAS_REG(__gx->IndTexScale1);
}
__gx->bpSent = 1;
}
void GXSetIndTexMtx(GXIndTexMtxID id, const void* offset, s8 scaleExp) {
CHECK(id >= GX_ITM_0 && id <= GX_ITM_2, "invalid ind tex mtx ID {}", static_cast<int>(id));
const auto* mtx = reinterpret_cast<const f32*>(offset);
s32 adjScale = scaleExp + 17;
// Write 3 BP registers for the 2x3 matrix
u32 idx = id - 1;
// Column 0: m[0][0] and m[1][0]
u32 reg0 = 0;
s32 m00 = static_cast<s32>(1024.0f * mtx[0]) & 0x7FF;
s32 m10 = static_cast<s32>(1024.0f * mtx[3]) & 0x7FF;
SET_REG_FIELD(0, reg0, 11, 0, m00);
SET_REG_FIELD(0, reg0, 11, 11, m10);
SET_REG_FIELD(0, reg0, 2, 22, adjScale & 3);
SET_REG_FIELD(0, reg0, 8, 24, idx * 3 + 6);
GX_WRITE_RAS_REG(reg0);
// Column 1: m[0][1] and m[1][1]
u32 reg1 = 0;
s32 m01 = static_cast<s32>(1024.0f * mtx[1]) & 0x7FF;
s32 m11 = static_cast<s32>(1024.0f * mtx[4]) & 0x7FF;
SET_REG_FIELD(0, reg1, 11, 0, m01);
SET_REG_FIELD(0, reg1, 11, 11, m11);
SET_REG_FIELD(0, reg1, 2, 22, (adjScale >> 2) & 3);
SET_REG_FIELD(0, reg1, 8, 24, idx * 3 + 7);
GX_WRITE_RAS_REG(reg1);
// Column 2: m[0][2] and m[1][2]
u32 reg2 = 0;
s32 m02 = static_cast<s32>(1024.0f * mtx[2]) & 0x7FF;
s32 m12 = static_cast<s32>(1024.0f * mtx[5]) & 0x7FF;
SET_REG_FIELD(0, reg2, 11, 0, m02);
SET_REG_FIELD(0, reg2, 11, 11, m12);
SET_REG_FIELD(0, reg2, 2, 22, (adjScale >> 4) & 3);
SET_REG_FIELD(0, reg2, 8, 24, idx * 3 + 8);
GX_WRITE_RAS_REG(reg2);
__gx->bpSent = 1;
}
void GXSetTevIndirect(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTexFormat fmt, GXIndTexBiasSel biasSel,
GXIndTexMtxID matrixSel, GXIndTexWrap wrapS, GXIndTexWrap wrapT, GXBool addPrev, GXBool indLod,
GXIndTexAlphaSel alphaSel) {
// BP register: tev_stage + 0x10
u32 reg = 0;
SET_REG_FIELD(0, reg, 2, 0, indStage);
SET_REG_FIELD(0, reg, 2, 2, fmt);
SET_REG_FIELD(0, reg, 3, 4, biasSel);
SET_REG_FIELD(0, reg, 2, 7, alphaSel);
SET_REG_FIELD(0, reg, 4, 9, matrixSel);
SET_REG_FIELD(0, reg, 3, 13, wrapS);
SET_REG_FIELD(0, reg, 3, 16, wrapT);
SET_REG_FIELD(0, reg, 1, 19, indLod);
SET_REG_FIELD(0, reg, 1, 20, addPrev);
SET_REG_FIELD(0, reg, 8, 24, tevStage + 16);
GX_WRITE_RAS_REG(reg);
__gx->bpSent = 1;
}
void GXSetTevDirect(GXTevStageID stageId) {
GXSetTevIndirect(stageId, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE,
GX_FALSE, GX_ITBA_OFF);
}
void GXSetTevIndWarp(GXTevStageID tevStage, GXIndTexStageID indStage, GXBool signedOffsets, GXBool replaceMode,
GXIndTexMtxID matrixSel) {
const auto wrap = replaceMode ? GX_ITW_0 : GX_ITW_OFF;
const auto biasSel = signedOffsets ? GX_ITB_STU : GX_ITB_NONE;
GXSetTevIndirect(tevStage, indStage, GX_ITF_8, biasSel, matrixSel, wrap, wrap, false, false, GX_ITBA_OFF);
}
void GXSetTevIndRepeat(GXTevStageID tevStage) {
GXSetTevIndirect(tevStage, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_0, GX_ITW_0, GX_TRUE,
GX_FALSE, GX_ITBA_OFF);
}
void GXSetTevIndBumpXYZ(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTexMtxID matrixSel) {
GXSetTevIndirect(tevStage, indStage, GX_ITF_8, GX_ITB_STU, matrixSel, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE, GX_FALSE,
GX_ITBA_OFF);
}
void GXSetTevIndBumpST(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTexMtxID matrixSel) {
GXIndTexMtxID sMtx, tMtx;
switch (matrixSel) {
case GX_ITM_0:
sMtx = GX_ITM_S0;
tMtx = GX_ITM_T0;
break;
case GX_ITM_1:
sMtx = GX_ITM_S1;
tMtx = GX_ITM_T1;
break;
case GX_ITM_2:
sMtx = GX_ITM_S2;
tMtx = GX_ITM_T2;
break;
default:
sMtx = GX_ITM_OFF;
tMtx = GX_ITM_OFF;
break;
}
// Stage 0: STU bias, S-dynamic matrix, wrap=0/0
GXSetTevIndirect(tevStage, indStage, GX_ITF_8, GX_ITB_STU, sMtx, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE,
GX_ITBA_OFF);
// Stage 1: STU bias, T-dynamic matrix, wrap=0/0, add prev
GXSetTevIndirect(static_cast<GXTevStageID>(tevStage + 1), indStage, GX_ITF_8, GX_ITB_STU, tMtx, GX_ITW_0, GX_ITW_0,
GX_TRUE, GX_FALSE, GX_ITBA_OFF);
// Stage 2: no bias, matrix off, wrap off, add prev
GXSetTevIndirect(static_cast<GXTevStageID>(tevStage + 2), indStage, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF,
GX_ITW_OFF, GX_TRUE, GX_FALSE, GX_ITBA_OFF);
}
void GXSetTevIndTile(GXTevStageID tevStage, GXIndTexStageID indStage, u16 tileSizeS, u16 tileSizeT, u16 tileSpacingS,
u16 tileSpacingT, GXIndTexFormat fmt, GXIndTexMtxID matrixSel, GXIndTexBiasSel biasSel,
GXIndTexAlphaSel alphaSel) {
auto sizeToWrap = [](u16 size) -> GXIndTexWrap {
switch (size) {
case 256: return GX_ITW_256;
case 128: return GX_ITW_128;
case 64: return GX_ITW_64;
case 32: return GX_ITW_32;
case 16: return GX_ITW_16;
default: return GX_ITW_0;
}
};
GXIndTexWrap wrapS = sizeToWrap(tileSizeS);
GXIndTexWrap wrapT = sizeToWrap(tileSizeT);
f32 mtx[2][3] = {};
mtx[0][0] = static_cast<f32>(tileSpacingS) / 1024.0f;
mtx[1][1] = static_cast<f32>(tileSpacingT) / 1024.0f;
GXSetIndTexMtx(matrixSel, mtx, 10);
GXSetTevIndirect(tevStage, indStage, fmt, biasSel, matrixSel, wrapS, wrapT, GX_FALSE, GX_TRUE, alphaSel);
}
}