#include "nw4r/g3d.h" // IWYU pragma: export #include "rvl/BASE.h" // IWYU pragma: export #include "rvl/GX.h" // IWYU pragma: export #include namespace nw4r { namespace g3d { bool ResTev::GXGetTevSwapModeTable( GXTevSwapSel swap, GXTevColorChan *pR, GXTevColorChan *pG, GXTevColorChan *pB, GXTevColorChan *pA ) const { const u8 *pCmd = ref().dl.dl.common.dl.swapModeTable[swap]; u32 cmd; if (pCmd[0] == 0) { return false; } detail::ResReadBPCmd(&pCmd[GX_BP_CMD_SZ * 1], &cmd); if (pR != NULL) { *pR = static_cast(cmd >> GX_BP_TEVKSEL_SWAP_RB_SHIFT & GX_BP_TEVKSEL_SWAP_RB_LMASK); } if (pG != NULL) { *pG = static_cast(cmd >> GX_BP_TEVKSEL_SWAP_GA_SHIFT & GX_BP_TEVKSEL_SWAP_GA_LMASK); } detail::ResReadBPCmd(&pCmd[GX_BP_CMD_SZ * 3], &cmd); if (pB != NULL) { *pB = static_cast(cmd >> GX_BP_TEVKSEL_SWAP_RB_SHIFT & GX_BP_TEVKSEL_SWAP_RB_LMASK); } if (pA != NULL) { *pA = static_cast(cmd >> GX_BP_TEVKSEL_SWAP_GA_SHIFT & GX_BP_TEVKSEL_SWAP_GA_LMASK); } return true; } void ResTev::GXSetTevSwapModeTable( GXTevSwapSel swap, GXTevColorChan r, GXTevColorChan g, GXTevColorChan b, GXTevColorChan a ) { u8 *pCmd = ref().dl.dl.common.dl.swapModeTable[swap]; u32 cmd; // clang-format off detail::ResWriteSSMask(&pCmd[GX_BP_CMD_SZ * 0], GX_BP_TEVKSEL_SWAP_RB_MASK | GX_BP_TEVKSEL_SWAP_GA_MASK); cmd = 0; cmd |= r << GX_BP_TEVKSEL_SWAP_RB_SHIFT; cmd |= g << GX_BP_TEVKSEL_SWAP_GA_SHIFT; cmd |= (swap * 2 + GX_BP_REG_TEVKSEL0) << GX_BP_OPCODE_SHIFT; detail::ResWriteBPCmd(&pCmd[GX_BP_CMD_SZ * 1], cmd, ~(GX_BP_TEVKSEL_KASEL_ODD_MASK | GX_BP_TEVKSEL_KCSEL_ODD_MASK | GX_BP_TEVKSEL_KASEL_EVEN_MASK | GX_BP_TEVKSEL_KCSEL_EVEN_MASK)); detail::ResWriteSSMask(&pCmd[GX_BP_CMD_SZ * 2], GX_BP_TEVKSEL_SWAP_RB_MASK | GX_BP_TEVKSEL_SWAP_GA_MASK); cmd = 0; cmd |= b << GX_BP_TEVKSEL_SWAP_RB_SHIFT; cmd |= a << GX_BP_TEVKSEL_SWAP_GA_SHIFT; cmd |= (swap * 2 + GX_BP_REG_TEVKSEL1) << GX_BP_OPCODE_SHIFT; detail::ResWriteBPCmd(&pCmd[GX_BP_CMD_SZ * 3], cmd, ~(GX_BP_TEVKSEL_KASEL_ODD_MASK | GX_BP_TEVKSEL_KCSEL_ODD_MASK | GX_BP_TEVKSEL_KASEL_EVEN_MASK | GX_BP_TEVKSEL_KCSEL_EVEN_MASK)); // clang-format on } bool ResTev::GXGetTevOrder(GXTevStageID stage, GXTexCoordID *pCoord, GXTexMapID *pMap, GXChannelID *pChannel) const { // Convert RAS channel ID to GX channel ID static const GXChannelID r2c[GX_RAS_MAX_CHANNEL] = {GX_COLOR0A0, GX_COLOR1A1, GX_COLOR_NULL, GX_COLOR_NULL, GX_COLOR_NULL, GX_ALPHA_BUMP, GX_ALPHA_BUMPN, GX_COLOR_ZERO}; const u8 *pCmd = ref().dl.dl.var[stage / TEV_STAGES_PER_DL].dl.tevOrder; if (pCmd[0] == 0) { return false; } u32 cmd; detail::ResReadBPCmd(pCmd, &cmd); bool enabled; GXTexCoordID coord; GXTexMapID map; GXChannelID channel; if (stage & 1) { channel = r2c[cmd >> GX_BP_RAS1_TREF_COLORCHAN_ODD_SHIFT & GX_BP_RAS1_TREF_COLORCHAN_ODD_LMASK]; coord = static_cast(cmd >> GX_BP_RAS1_TREF_TEXCOORD_ODD_SHIFT & GX_BP_RAS1_TREF_TEXCOORD_ODD_LMASK); enabled = cmd >> GX_BP_RAS1_TREF_ENABLE_TEX_ODD_SHIFT & GX_BP_RAS1_TREF_ENABLE_TEX_ODD_LMASK; map = static_cast(cmd >> GX_BP_RAS1_TREF_TEXMAP_ODD_SHIFT & GX_BP_RAS1_TREF_TEXMAP_ODD_LMASK); } else { channel = r2c[cmd >> GX_BP_RAS1_TREF_COLORCHAN_EVEN_SHIFT & GX_BP_RAS1_TREF_COLORCHAN_EVEN_LMASK]; coord = static_cast(cmd >> GX_BP_RAS1_TREF_TEXCOORD_EVEN_SHIFT & GX_BP_RAS1_TREF_TEXCOORD_EVEN_LMASK); enabled = cmd >> GX_BP_RAS1_TREF_ENABLE_TEX_EVEN_SHIFT & GX_BP_RAS1_TREF_ENABLE_TEX_EVEN_LMASK; map = static_cast(cmd >> GX_BP_RAS1_TREF_TEXMAP_EVEN_SHIFT & GX_BP_RAS1_TREF_TEXMAP_EVEN_LMASK); } if (pCoord != NULL) { *pCoord = coord; } if (pChannel != NULL) { *pChannel = channel; } if (!enabled) { map = GX_TEXMAP_NULL; } if (pMap) { *pMap = map; } return true; } void ResTev::GXSetTevOrder(GXTevStageID stage, GXTexCoordID coord, GXTexMapID map, GXChannelID channel) { static u8 c2r[0x10] = {0, 1, 0, 1, 0, 1, 7, 5, 6, 0, 0, 0, 0, 0, 0, 7}; GXTexCoordID currCoord; GXTexMapID currMap; if (GXGetTevOrder(stage, &currCoord, &currMap, NULL)) { if (currCoord != GX_TEXCOORD_NULL && currMap != GX_TEXMAP_NULL) { ref().texCoordToTexMapID[currCoord] = GX_TEXMAP_NULL; } } if (coord != GX_TEXCOORD_NULL) { ref().texCoordToTexMapID[coord] = map; } u8 *pCmd = ref().dl.dl.var[stage / TEV_STAGES_PER_DL].dl.tevOrder; // Use the shift to determine if Even or Odd u32 shift = ((stage & 1) ? GX_BP_RAS1_TREF_TEXMAP_ODD_SHIFT : GX_BP_RAS1_TREF_TEXMAP_EVEN_SHIFT); // clang-format off u32 mask = ( (0xFF << 24) | ( GX_BP_RAS1_TREF_TEXMAP_EVEN_MASK | GX_BP_RAS1_TREF_TEXCOORD_EVEN_MASK | GX_BP_RAS1_TREF_ENABLE_TEX_EVEN_MASK | GX_BP_RAS1_TREF_COLORCHAN_EVEN_MASK ) << shift ); u32 cmd = ( ( ( map & GX_BP_RAS1_TREF_TEXMAP_EVEN_LMASK ) << GX_BP_RAS1_TREF_TEXMAP_EVEN_SHIFT ) | ( ( coord & GX_BP_RAS1_TREF_TEXCOORD_EVEN_LMASK ) << GX_BP_RAS1_TREF_TEXCOORD_EVEN_SHIFT ) | ( ( map != GX_TEXMAP_NULL && !(map & GX_TEX_DISABLE) ) << GX_BP_RAS1_TREF_ENABLE_TEX_EVEN_SHIFT ) | ( ( c2r[channel & 0xF] ) << GX_BP_RAS1_TREF_COLORCHAN_EVEN_SHIFT ) ); // clang-format on cmd <<= shift; cmd |= (GX_BP_REG_RAS1_TREF0 + (stage / TEV_STAGES_PER_DL)) << 24; detail::ResWriteBPCmd(pCmd, cmd, mask); } void ResTev::GXSetTevColorIn(GXTevStageID stage, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d) { u8 *pCmd = ref().dl.dl.var[stage / TEV_STAGES_PER_DL].dl.tevColorCalc[stage % TEV_STAGES_PER_DL]; // clang-format off detail::ResWriteBPCmd(pCmd, (d << GX_BP_TEVCOLORCOMBINER_D_SHIFT) | (c << GX_BP_TEVCOLORCOMBINER_C_SHIFT) | (b << GX_BP_TEVCOLORCOMBINER_B_SHIFT) | (a << GX_BP_TEVCOLORCOMBINER_A_SHIFT) | ((GX_BP_REG_TEVCOLORCOMBINER0 + stage * 2) << GX_BP_OPCODE_SHIFT), ~(GX_BP_TEVCOLORCOMBINER_DEST_MASK | GX_BP_TEVCOLORCOMBINER_SCALE_OR_COMPARE_MODE_MASK | GX_BP_TEVCOLORCOMBINER_CLAMP_MASK | GX_BP_TEVCOLORCOMBINER_OP_OR_COMPARISON_MASK | GX_BP_TEVCOLORCOMBINER_BIAS_MASK)); // clang-format on } bool ResTev::GXGetIndTexOrder(GXIndTexStageID stage, GXTexCoordID *pCoord, GXTexMapID *pMap) const { const u8 *pCmd = ref().dl.dl.common.dl.indTexOrder[stage / GX_MAX_INDTEXSTAGE]; if (pCmd[0] == 0) { return false; } u32 cmd; detail::ResReadBPCmd(pCmd, &cmd); u32 shift = (stage % GX_MAX_INDTEXSTAGE) * (GX_BP_RAS1_IREF_MAP0_SZ + GX_BP_RAS1_IREF_TXC0_SZ); if (pMap) { *pMap = static_cast(cmd >> (shift + GX_BP_RAS1_IREF_MAP0_SHIFT) & GX_BP_RAS1_IREF_MAP0_LMASK); } if (pCoord) { *pCoord = static_cast(cmd >> (shift + GX_BP_RAS1_IREF_TXC0_SHIFT) & GX_BP_RAS1_IREF_TXC0_LMASK); } return true; } void ResTev::GXSetIndTexOrder(GXIndTexStageID stage, GXTexCoordID coord, GXTexMapID map) { GXTexCoordID currCoord; GXTexMapID currMap; if (GXGetIndTexOrder(stage, &currCoord, &currMap)) { if (currCoord != GX_TEXCOORD_NULL && currMap != GX_TEXMAP_NULL) { ref().texCoordToTexMapID[currCoord] = GX_TEXMAP_NULL; } } if (coord != GX_TEXCOORD_NULL) { ref().texCoordToTexMapID[coord] = map; } u8 *pCmd = ref().dl.dl.common.dl.indTexOrder[stage / GX_MAX_INDTEXSTAGE]; u32 shift = (stage % GX_MAX_INDTEXSTAGE) * (GX_BP_RAS1_IREF_MAP0_SZ + GX_BP_RAS1_IREF_TXC0_SZ); u32 reg = ((map & GX_BP_RAS1_IREF_MAP0_LMASK) << (shift + GX_BP_RAS1_IREF_MAP0_SHIFT)) | ((coord & GX_BP_RAS1_IREF_TXC0_LMASK) << (shift + GX_BP_RAS1_IREF_TXC0_SHIFT)) | (GX_BP_REG_RAS1_IREF << 24); u32 mask = ((GX_BP_RAS1_IREF_MAP0_LMASK) << (shift + GX_BP_RAS1_IREF_MAP0_SHIFT)) | ((GX_BP_RAS1_IREF_TXC0_LMASK) << (shift + GX_BP_RAS1_IREF_TXC0_SHIFT)); detail::ResWriteBPCmd(pCmd, reg, mask); } void ResTev::CallDisplayList(bool sync) const { // Variable DL holds data for two GX tev stages static const u32 dlsize[GX_MAX_TEVSTAGE] = { ROUND_UP(sizeof(ResTevCommonDL) + 1 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 1 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 2 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 2 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 3 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 3 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 4 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 4 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 5 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 5 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 6 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 6 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 7 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 7 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 8 * sizeof(ResTevVariableDL), 32), ROUND_UP(sizeof(ResTevCommonDL) + 8 * sizeof(ResTevVariableDL), 32) }; if (sync) { PPCSync(); } GXCallDisplayList(const_cast(&ref().dl), dlsize[GetNumTevStages() - 1]); } ResTev ResTev::CopyTo(void *pDst) { const ResTevData *pSrc = &ref(); detail::Copy32ByteBlocks(pDst, pSrc, sizeof(ResTevData)); ResTev tev(pDst); tev.ref().toResMdlData -= reinterpret_cast(pDst) - reinterpret_cast(pSrc); tev.DCStore(false); return tev; } void ResTev::DCStore(bool sync) { void *pBase = &ref(); u32 size = ref().size; if (sync) { DC::StoreRange(pBase, size); } else { DC::StoreRangeNoSync(pBase, size); } } } // namespace g3d } // namespace nw4r