mirror of
https://github.com/blawar/GLideN64.git
synced 2024-07-04 10:03:36 +00:00
1045 lines
32 KiB
C++
1045 lines
32 KiB
C++
#include <assert.h>
|
|
#include <algorithm>
|
|
#include "S2DEX.h"
|
|
#include "F3D.h"
|
|
#include "F3DEX.h"
|
|
#include "GBI.h"
|
|
#include "gSP.h"
|
|
#include "gDP.h"
|
|
#include "RSP.h"
|
|
#include "RDP.h"
|
|
#include "Config.h"
|
|
#include "Log.h"
|
|
#include "DebugDump.h"
|
|
#include "DepthBuffer.h"
|
|
#include "FrameBuffer.h"
|
|
|
|
#include <Graphics/Context.h>
|
|
#include <Graphics/Parameters.h>
|
|
#include "DisplayWindow.h"
|
|
|
|
using namespace graphics;
|
|
|
|
#define S2DEX_MV_MATRIX 0
|
|
#define S2DEX_MV_SUBMUTRIX 2
|
|
#define S2DEX_MV_VIEWPORT 8
|
|
|
|
#define S2DEX_BG_1CYC 0x01
|
|
#define S2DEX_BG_COPY 0x02
|
|
#define S2DEX_OBJ_RECTANGLE 0x03
|
|
#define S2DEX_OBJ_SPRITE 0x04
|
|
#define S2DEX_OBJ_MOVEMEM 0x05
|
|
#define S2DEX_LOAD_UCODE 0xAF
|
|
#define S2DEX_SELECT_DL 0xB0
|
|
#define S2DEX_OBJ_RENDERMODE 0xB1
|
|
#define S2DEX_OBJ_RECTANGLE_R 0xB2
|
|
#define S2DEX_OBJ_LOADTXTR 0xC1
|
|
#define S2DEX_OBJ_LDTX_SPRITE 0xC2
|
|
#define S2DEX_OBJ_LDTX_RECT 0xC3
|
|
#define S2DEX_OBJ_LDTX_RECT_R 0xC4
|
|
#define S2DEX_RDPHALF_0 0xE4
|
|
|
|
struct uObjScaleBg
|
|
{
|
|
u16 imageW; /* Texture width (8-byte alignment, u10.2) */
|
|
u16 imageX; /* x-coordinate of upper-left
|
|
position of texture (u10.5) */
|
|
u16 frameW; /* Transfer destination frame width (u10.2) */
|
|
s16 frameX; /* x-coordinate of upper-left
|
|
position of transfer destination frame (s10.2) */
|
|
|
|
u16 imageH; /* Texture height (u10.2) */
|
|
u16 imageY; /* y-coordinate of upper-left position of
|
|
texture (u10.5) */
|
|
u16 frameH; /* Transfer destination frame height (u10.2) */
|
|
s16 frameY; /* y-coordinate of upper-left position of transfer
|
|
destination frame (s10.2) */
|
|
|
|
u32 imagePtr; /* Address of texture source in DRAM*/
|
|
u8 imageSiz; /* Texel size
|
|
G_IM_SIZ_4b (4 bits/texel)
|
|
G_IM_SIZ_8b (8 bits/texel)
|
|
G_IM_SIZ_16b (16 bits/texel)
|
|
G_IM_SIZ_32b (32 bits/texel) */
|
|
u8 imageFmt; /*Texel format
|
|
G_IM_FMT_RGBA (RGBA format)
|
|
G_IM_FMT_YUV (YUV format)
|
|
G_IM_FMT_CI (CI format)
|
|
G_IM_FMT_IA (IA format)
|
|
G_IM_FMT_I (I format) */
|
|
u16 imageLoad; /* Method for loading the BG image texture
|
|
G_BGLT_LOADBLOCK (use LoadBlock)
|
|
G_BGLT_LOADTILE (use LoadTile) */
|
|
u16 imageFlip; /* Image inversion on/off (horizontal
|
|
direction only)
|
|
0 (normal display (no inversion))
|
|
G_BG_FLAG_FLIPS (horizontal inversion of texture image) */
|
|
u16 imagePal; /* Position of palette for 4-bit color
|
|
index texture (4-bit precision, 0~15) */
|
|
|
|
u16 scaleH; /* y-direction scale value (u5.10) */
|
|
u16 scaleW; /* x-direction scale value (u5.10) */
|
|
s32 imageYorig; /* image drawing origin (s20.5)*/
|
|
|
|
u8 padding[4]; /* Padding */
|
|
}; /* 40 bytes */
|
|
|
|
struct uObjSprite
|
|
{
|
|
u16 scaleW; /* Width-direction scaling (u5.10) */
|
|
s16 objX; /* x-coordinate of upper-left corner of OBJ (s10.2) */
|
|
u16 paddingX; /* Unused (always 0) */
|
|
u16 imageW; /* Texture width (length in s direction, u10.5) */
|
|
u16 scaleH; /* Height-direction scaling (u5.10) */
|
|
s16 objY; /* y-coordinate of upper-left corner of OBJ (s10.2) */
|
|
u16 paddingY; /* Unused (always 0) */
|
|
u16 imageH; /* Texture height (length in t direction, u10.5) */
|
|
u16 imageAdrs; /* Texture starting position in TMEM (In units of 64-bit words) */
|
|
u16 imageStride; /* Texel wrapping width (In units of 64-bit words) */
|
|
u8 imageFlags; /* Display flag
|
|
(*) More than one of the following flags can be specified as the bit sum of the flags:
|
|
0 (Normal display (no inversion))
|
|
G_OBJ_FLAG_FLIPS (s-direction (x) inversion)
|
|
G_OBJ_FLAG_FLIPT (t-direction (y) inversion) */
|
|
u8 imagePal; /* Position of palette for 4-bit color index texture (4-bit precision, 0~7) */
|
|
u8 imageSiz; /* Texel size
|
|
G_IM_SIZ_4b (4 bits/texel)
|
|
G_IM_SIZ_8b (8 bits/texel)
|
|
G_IM_SIZ_16b (16 bits/texel)
|
|
G_IM_SIZ_32b (32 bits/texel) */
|
|
u8 imageFmt; /* Texel format
|
|
G_IM_FMT_RGBA (RGBA format)
|
|
G_IM_FMT_YUV (YUV format)
|
|
G_IM_FMT_CI (CI format)
|
|
G_IM_FMT_IA (IA format)
|
|
G_IM_FMT_I (I format) */
|
|
}; /* 24 bytes */
|
|
|
|
struct uObjTxtrBlock
|
|
{
|
|
u32 type; /* Structure identifier (G_OBJLT_TXTRBLOCK) */
|
|
u32 image; /* Texture source address in DRAM (8-byte alignment) */
|
|
u16 tsize; /* Texture size (specified by GS_TB_TSIZE) */
|
|
u16 tmem; /* TMEM word address where texture will be loaded (8-byte word) */
|
|
u16 sid; /* Status ID (multiple of 4: either 0, 4, 8, or 12) */
|
|
u16 tline; /* Texture line width (specified by GS_TB_TLINE) */
|
|
u32 flag; /* Status flag */
|
|
u32 mask; /* Status mask */
|
|
}; /* 24 bytes */
|
|
|
|
struct uObjTxtrTile
|
|
{
|
|
u32 type; /* Structure identifier (G_OBJLT_TXTRTILE) */
|
|
u32 image; /* Texture source address in DRAM (8-byte alignment) */
|
|
u16 twidth; /* Texture width (specified by GS_TT_TWIDTH) */
|
|
u16 tmem; /* TMEM word address where texture will be loaded (8-byte word) */
|
|
u16 sid; /* Status ID (multiple of 4: either 0, 4, 8, or 12) */
|
|
u16 theight;/* Texture height (specified by GS_TT_THEIGHT) */
|
|
u32 flag; /* Status flag */
|
|
u32 mask; /* Status mask */
|
|
}; /* 24 bytes */
|
|
|
|
struct uObjTxtrTLUT
|
|
{
|
|
u32 type; /* Structure identifier (G_OBJLT_TLUT) */
|
|
u32 image; /* Texture source address in DRAM */
|
|
u16 pnum; /* Number of palettes to load - 1 */
|
|
u16 phead; /* Palette position at start of load (256~511) */
|
|
u16 sid; /* Status ID (multiple of 4: either 0, 4, 8, or 12) */
|
|
u16 zero; /* Always assign 0 */
|
|
u32 flag; /* Status flag */
|
|
u32 mask; /* Status mask */
|
|
}; /* 24 bytes */
|
|
|
|
typedef union
|
|
{
|
|
uObjTxtrBlock block;
|
|
uObjTxtrTile tile;
|
|
uObjTxtrTLUT tlut;
|
|
} uObjTxtr;
|
|
|
|
struct uObjTxSprite
|
|
{
|
|
uObjTxtr txtr;
|
|
uObjSprite sprite;
|
|
};
|
|
|
|
struct uObjMtx
|
|
{
|
|
s32 A, B, C, D; /* s15.16 */
|
|
s16 Y, X; /* s10.2 */
|
|
u16 BaseScaleY; /* u5.10 */
|
|
u16 BaseScaleX; /* u5.10 */
|
|
};
|
|
|
|
struct uObjSubMtx
|
|
{
|
|
s16 Y, X; /* s10.2 */
|
|
u16 BaseScaleY; /* u5.10 */
|
|
u16 BaseScaleX; /* u5.10 */
|
|
};
|
|
|
|
static uObjMtx objMtx;
|
|
|
|
void resetObjMtx()
|
|
{
|
|
objMtx.A = 1 << 16;
|
|
objMtx.B = 0;
|
|
objMtx.C = 0;
|
|
objMtx.D = 1 << 16;
|
|
objMtx.X = 0;
|
|
objMtx.Y = 0;
|
|
objMtx.BaseScaleX = 1 << 10;
|
|
objMtx.BaseScaleY = 1 << 10;
|
|
}
|
|
|
|
static
|
|
bool gs_bVer1_3 = true;
|
|
|
|
struct S2DEXCoordCorrector
|
|
{
|
|
S2DEXCoordCorrector()
|
|
{
|
|
static const u32 CorrectorsA01[] = {
|
|
0x00000000,
|
|
0x00100020,
|
|
0x00200040,
|
|
0x00300060,
|
|
0x0000FFF4,
|
|
0x00100014,
|
|
0x00200034,
|
|
0x00300054
|
|
};
|
|
static const s16 * CorrectorsA01_16 = reinterpret_cast<const s16*>(CorrectorsA01);
|
|
|
|
static const u32 CorrectorsA23[] = {
|
|
0x0001FFFE,
|
|
0xFFFEFFFE,
|
|
0x00010000,
|
|
0x00000000
|
|
};
|
|
static const s16 * CorrectorsA23_16 = reinterpret_cast<const s16*>(CorrectorsA23);
|
|
|
|
const u32 O1 = (gSP.objRendermode & (G_OBJRM_SHRINKSIZE_1 | G_OBJRM_SHRINKSIZE_2 | G_OBJRM_WIDEN)) >> 3;
|
|
A0 = CorrectorsA01_16[(0 + O1) ^ 1];
|
|
A1 = CorrectorsA01_16[(1 + O1) ^ 1];
|
|
const u32 O2 = (gSP.objRendermode & (G_OBJRM_SHRINKSIZE_1 | G_OBJRM_BILERP)) >> 2;
|
|
A2 = CorrectorsA23_16[(0 + O2) ^ 1];
|
|
A3 = CorrectorsA23_16[(1 + O2) ^ 1];
|
|
|
|
const s16 * CorrectorsB03_16 = nullptr;
|
|
u32 O3 = 0;
|
|
if (gs_bVer1_3) {
|
|
static const u32 CorrectorsB03_v1_3[] = {
|
|
0xFFFC0000,
|
|
0x00000000,
|
|
0x00000001,
|
|
0x00000000,
|
|
0xFFFC0000,
|
|
0x00000000,
|
|
0x00000001,
|
|
0xFFFF0001,
|
|
0xFFFC0000,
|
|
0x00030000,
|
|
0x00000001,
|
|
0x00000000,
|
|
0xFFFC0000,
|
|
0x00030000,
|
|
0x00000001,
|
|
0xFFFF0000,
|
|
0xFFFF0003,
|
|
0x0000FFF0,
|
|
0x00000001,
|
|
0x0000FFFF,
|
|
0xFFFF0003,
|
|
0x0000FFF0,
|
|
0x00000001,
|
|
0xFFFFFFFF,
|
|
0xFFFF0003,
|
|
0x0000FFF0,
|
|
0x00000000,
|
|
0x00000000,
|
|
0xFFFF0003,
|
|
0x0000FFF0,
|
|
0x00000000,
|
|
0xFFFF0000
|
|
};
|
|
CorrectorsB03_16 = reinterpret_cast<const s16*>(CorrectorsB03_v1_3);
|
|
O3 = (_SHIFTL(gSP.objRendermode, 3, 16) & (G_OBJRM_SHRINKSIZE_1 | G_OBJRM_SHRINKSIZE_2 | G_OBJRM_WIDEN)) >> 1;
|
|
} else {
|
|
static const u32 CorrectorsB03[] = {
|
|
0xFFFC0000,
|
|
0x00000001,
|
|
0xFFFF0003,
|
|
0xFFF00000
|
|
};
|
|
CorrectorsB03_16 = reinterpret_cast<const s16*>(CorrectorsB03);
|
|
O3 = (gSP.objRendermode & G_OBJRM_BILERP) >> 1;
|
|
}
|
|
B0 = CorrectorsB03_16[(0 + O3) ^ 1];
|
|
B2 = CorrectorsB03_16[(2 + O3) ^ 1];
|
|
B3 = CorrectorsB03_16[(3 + O3) ^ 1];
|
|
}
|
|
|
|
s16 A0, A1, A2, A3, B0, B2, B3;
|
|
};
|
|
|
|
struct ObjCoordinates
|
|
{
|
|
f32 ulx, uly, lrx, lry;
|
|
f32 uls, ult, lrs, lrt;
|
|
f32 z, w;
|
|
|
|
ObjCoordinates(const uObjSprite *_pObjSprite, bool _useMatrix)
|
|
{
|
|
/* Fixed point coordinates calculation. Decoded by olivieryuyu */
|
|
S2DEXCoordCorrector CC;
|
|
s16 xh, xl, yh, yl;
|
|
s16 sh, sl, th, tl;
|
|
auto calcST = [&](s16 B, u32 scaleH) {
|
|
sh = CC.A0 + B;
|
|
sl = sh + _pObjSprite->imageW + CC.A0 - CC.A1 - 1;
|
|
th = sh - (((yh & 3) * 0x0200 * scaleH) >> 16);
|
|
tl = th + _pObjSprite->imageH + CC.A0 - CC.A1 - 1;
|
|
};
|
|
if (_useMatrix) {
|
|
const u32 scaleW = (u32(objMtx.BaseScaleX) * 0x40 * _pObjSprite->scaleW) >> 16;
|
|
const u32 scaleH = (u32(objMtx.BaseScaleY) * 0x40 * _pObjSprite->scaleH) >> 16;
|
|
if (gs_bVer1_3) {
|
|
// XH = AND((((objX * 0x0800) << 16) / ((BaseScaleX * 2 - 0x02) >> 16) + X + A2) by B0
|
|
// XL = XH + AND(((imageW - A1) * 0x100) / (scaleW * 2 - 0x02) + B2) by B0
|
|
// YH = AND((((objY * 0x0800) << 16) / ((BaseScaleY * 2 - 0x02) >> 16) + Y + A2) by B0
|
|
// YL = YH + AND(((imageH - A1) * 0x100) / (scaleH * 2 - 0x02) + B2) by B0
|
|
xh = static_cast<s16>(((((s64(_pObjSprite->objX) << 27) / ((objMtx.BaseScaleX - 1) << 1)) >> 16) + objMtx.X + CC.A2) & CC.B0);
|
|
xl = static_cast<s16>((((u32(_pObjSprite->imageW) - CC.A1) << 8) / ((scaleW - 1) << 1) /*+ CC.B2*/) & CC.B0) + xh;
|
|
yh = static_cast<s16>(((((s64(_pObjSprite->objY) << 27) / ((objMtx.BaseScaleY - 1) << 1)) >> 16) + objMtx.Y + CC.A2) & CC.B0);
|
|
yl = static_cast<s16>((((u32(_pObjSprite->imageH) - CC.A1) << 8) / ((scaleH - 1) << 1) /*+ CC.B2*/) & CC.B0) + yh;
|
|
calcST(CC.B3, scaleH);
|
|
} else {
|
|
// XHP = ((objX << 16) * 0x0800 * (0x80007FFF / BaseScaleX)) >> 16 + ((AND(X + A2) by B0) << 16))
|
|
// XH = XHP >> 16
|
|
// XLP = XHP + (((ImageW - A1) << 24) * (0x80007FFF / scaleW)) >> 32
|
|
// XL = XLP >> 16
|
|
// YHP = ((objY << 16) * 0x0800 * (0x80007FFF / BaseScaleY)) >> 16 + ((AND(Y + A2) by B0) << 16))
|
|
// YH = YHP >> 16
|
|
// YLP = YHP + (((ImageH - A1) << 24) * (0x80007FFF / scaleH)) >> 32
|
|
// YL = YLP >> 16
|
|
const s32 xhp = ((((s64(_pObjSprite->objX) << 16) * 0x0800) * (0x80007FFFU / u32(objMtx.BaseScaleX))) >> 32) + (((objMtx.X + CC.A2) & CC.B0) << 16);
|
|
xh = static_cast<s16>(xhp >> 16);
|
|
const s32 xlp = xhp + ((((u64(_pObjSprite->imageW) - CC.A1) << 24) * (0x80007FFFU / scaleW)) >> 32);
|
|
xl = static_cast<s16>(xlp >> 16);
|
|
const s32 yhp = ((((s64(_pObjSprite->objY) << 16) * 0x0800) * (0x80007FFFU / u32(objMtx.BaseScaleY))) >> 32) + (((objMtx.Y + CC.A2) & CC.B0) << 16);
|
|
yh = static_cast<s16>(yhp >> 16);
|
|
const s32 ylp = yhp + ((((u64(_pObjSprite->imageH) - CC.A1) << 24) * (0x80007FFFU / scaleH)) >> 32);
|
|
yl = static_cast<s16>(ylp >> 16);
|
|
calcST(CC.B2, scaleH);
|
|
}
|
|
} else {
|
|
// XH = AND(objX + A2) by B0
|
|
// XL = ((AND(objX + A2) by B0) << 16) + (((ImageW - A1) << 24)*(0x80007FFF / scaleW)) >> 48
|
|
// YH = AND(objY + A2) by B0
|
|
// YL = ((AND(objY + A2) by B0) << 16) + (((ImageH - A1) << 24)*(0x80007FFF / scaleH)) >> 48
|
|
xh = (_pObjSprite->objX + CC.A2) & CC.B0;
|
|
xl = static_cast<s16>((((u64(_pObjSprite->imageW) - CC.A1) << 24) * (0x80007FFFU / u32(_pObjSprite->scaleW))) >> 48) + xh;
|
|
yh = (_pObjSprite->objY + CC.A2) & CC.B0;
|
|
yl = static_cast<s16>((((u64(_pObjSprite->imageH) - CC.A1) << 24) * (0x80007FFFU / u32(_pObjSprite->scaleH))) >> 48) + yh;
|
|
calcST(CC.B2, _pObjSprite->scaleH);
|
|
}
|
|
|
|
ulx = _FIXED2FLOAT(xh, 2);
|
|
lrx = _FIXED2FLOAT(xl, 2);
|
|
uly = _FIXED2FLOAT(yh, 2);
|
|
lry = _FIXED2FLOAT(yl, 2);
|
|
|
|
uls = _FIXED2FLOAT(sh, 5);
|
|
lrs = _FIXED2FLOAT(sl, 5);
|
|
ult = _FIXED2FLOAT(th, 5);
|
|
lrt = _FIXED2FLOAT(tl, 5);
|
|
|
|
if ((_pObjSprite->imageFlags & G_BG_FLAG_FLIPS) != 0)
|
|
std::swap(uls, lrs);
|
|
if ((_pObjSprite->imageFlags & G_BG_FLAG_FLIPT) != 0)
|
|
std::swap(ult, lrt);
|
|
|
|
z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
|
|
w = 1.0f;
|
|
}
|
|
|
|
ObjCoordinates(const uObjScaleBg * _pObjScaleBg)
|
|
{
|
|
const f32 frameX = _FIXED2FLOAT(_pObjScaleBg->frameX, 2);
|
|
const f32 frameY = _FIXED2FLOAT(_pObjScaleBg->frameY, 2);
|
|
const f32 imageX = gSP.bgImage.imageX;
|
|
const f32 imageY = gSP.bgImage.imageY;
|
|
f32 scaleW = gSP.bgImage.scaleW;
|
|
f32 scaleH = gSP.bgImage.scaleH;
|
|
|
|
// gSPBgRectCopy() does not support scaleW and scaleH
|
|
if (gDP.otherMode.cycleType == G_CYC_COPY) {
|
|
scaleW = 1.0f;
|
|
scaleH = 1.0f;
|
|
}
|
|
|
|
f32 frameW = _FIXED2FLOAT(_pObjScaleBg->frameW, 2);
|
|
f32 frameH = _FIXED2FLOAT(_pObjScaleBg->frameH, 2);
|
|
f32 imageW = (f32)(_pObjScaleBg->imageW >> 2);
|
|
f32 imageH = (f32)(_pObjScaleBg->imageH >> 2);
|
|
// const f32 imageW = (f32)gSP.bgImage.width;
|
|
// const f32 imageH = (f32)gSP.bgImage.height;
|
|
|
|
if (u32(imageW) == 512 && (config.generalEmulation.hacks & hack_RE2) != 0) {
|
|
const f32 width = f32(*REG.VI_WIDTH);
|
|
const f32 scale = imageW / width;
|
|
imageW = width;
|
|
frameW = width;
|
|
imageH *= scale;
|
|
frameH *= scale;
|
|
scaleW = 1.0f;
|
|
scaleH = 1.0f;
|
|
}
|
|
|
|
uls = imageX;
|
|
ult = imageY;
|
|
lrs = uls + std::min(imageW, frameW * scaleW) - 1;
|
|
lrt = ult + std::min(imageH, frameH * scaleH) - 1;
|
|
|
|
// G_CYC_COPY (gSPBgRectCopy()) does not allow texture filtering
|
|
if (gDP.otherMode.cycleType != G_CYC_COPY) {
|
|
// Correct texture coordinates -0.5f and +0.5 if G_OBJRM_BILERP
|
|
// bilinear interpolation is set
|
|
if ((gSP.objRendermode&G_OBJRM_BILERP) != 0 &&
|
|
((gDP.otherMode.textureFilter == G_TF_BILERP) || // Kirby Crystal Shards
|
|
(gDP.otherMode.textureFilter == G_TF_POINT && (gSP.objRendermode&G_OBJRM_NOTXCLAMP) != 0)) // Worms Armageddon
|
|
) {
|
|
uls -= 0.5f;
|
|
ult -= 0.5f;
|
|
lrs += 0.5f;
|
|
lrt += 0.5f;
|
|
}
|
|
// SHRINKSIZE_1 adds a 0.5f perimeter around the image
|
|
// upper left texture coords += 0.5f; lower left texture coords -= 0.5f
|
|
if ((gSP.objRendermode&G_OBJRM_SHRINKSIZE_1) != 0) {
|
|
uls += 0.5f;
|
|
ult += 0.5f;
|
|
lrs -= 0.5f;
|
|
lrt -= 0.5f;
|
|
// SHRINKSIZE_2 adds a 1.0f perimeter
|
|
// upper left texture coords += 1.0f; lower left texture coords -= 1.0f
|
|
}
|
|
else if ((gSP.objRendermode&G_OBJRM_SHRINKSIZE_2) != 0) {
|
|
uls += 1.0f;
|
|
ult += 1.0f;
|
|
lrs -= 1.0f;
|
|
lrt -= 1.0f;
|
|
}
|
|
}
|
|
|
|
// Calculate lrx and lry width new ST values
|
|
ulx = frameX;
|
|
uly = frameY;
|
|
lrx = ulx + (lrs - uls) / scaleW;
|
|
lry = uly + (lrt - ult) / scaleH;
|
|
if ((gSP.objRendermode&G_OBJRM_BILERP) == 0) {
|
|
lrx += 1.0f / scaleW;
|
|
lry += 1.0f / scaleH;
|
|
}
|
|
|
|
// gSPBgRect1Cyc() and gSPBgRectCopy() do only support
|
|
// imageFlip in horizontal direction
|
|
if ((_pObjScaleBg->imageFlip & G_BG_FLAG_FLIPS) != 0) {
|
|
std::swap(ulx, lrx);
|
|
}
|
|
|
|
z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
|
|
w = 1.0f;
|
|
}
|
|
};
|
|
|
|
static
|
|
u16 _YUVtoRGBA(u8 y, u8 u, u8 v)
|
|
{
|
|
float r = y + (1.370705f * (v - 128));
|
|
float g = y - (0.698001f * (v - 128)) - (0.337633f * (u - 128));
|
|
float b = y + (1.732446f * (u - 128));
|
|
r *= 0.125f;
|
|
g *= 0.125f;
|
|
b *= 0.125f;
|
|
//clipping the result
|
|
if (r > 31) r = 31;
|
|
if (g > 31) g = 31;
|
|
if (b > 31) b = 31;
|
|
if (r < 0) r = 0;
|
|
if (g < 0) g = 0;
|
|
if (b < 0) b = 0;
|
|
|
|
u16 c = (u16)(((u16)(r) << 11) |
|
|
((u16)(g) << 6) |
|
|
((u16)(b) << 1) | 1);
|
|
return c;
|
|
}
|
|
|
|
static
|
|
void _drawYUVImageToFrameBuffer(const ObjCoordinates & _objCoords)
|
|
{
|
|
const u32 ulx = (u32)_objCoords.ulx;
|
|
const u32 uly = (u32)_objCoords.uly;
|
|
const u32 lrx = (u32)_objCoords.lrx;
|
|
const u32 lry = (u32)_objCoords.lry;
|
|
const u32 ci_width = gDP.colorImage.width;
|
|
const u32 ci_height = (u32)gDP.scissor.lry;
|
|
if (ulx >= ci_width)
|
|
return;
|
|
if (uly >= ci_height)
|
|
return;
|
|
u32 width = 16, height = 16;
|
|
if (lrx > ci_width)
|
|
width = ci_width - ulx;
|
|
if (lry > ci_height)
|
|
height = ci_height - uly;
|
|
u32 * mb = (u32*)(RDRAM + gDP.textureImage.address); //pointer to the first macro block
|
|
u16 * dst = (u16*)(RDRAM + gDP.colorImage.address);
|
|
dst += ulx + uly * ci_width;
|
|
//yuv macro block contains 16x16 texture. we need to put it in the proper place inside cimg
|
|
for (u16 h = 0; h < 16; h++) {
|
|
for (u16 w = 0; w < 16; w += 2) {
|
|
u32 t = *(mb++); //each u32 contains 2 pixels
|
|
if ((h < height) && (w < width)) //clipping. texture image may be larger than color image
|
|
{
|
|
u8 y0 = (u8)t & 0xFF;
|
|
u8 v = (u8)(t >> 8) & 0xFF;
|
|
u8 y1 = (u8)(t >> 16) & 0xFF;
|
|
u8 u = (u8)(t >> 24) & 0xFF;
|
|
*(dst++) = _YUVtoRGBA(y0, u, v);
|
|
*(dst++) = _YUVtoRGBA(y1, u, v);
|
|
}
|
|
}
|
|
dst += ci_width - 16;
|
|
}
|
|
FrameBuffer *pBuffer = frameBufferList().getCurrent();
|
|
if (pBuffer != nullptr)
|
|
pBuffer->m_isOBScreen = true;
|
|
}
|
|
|
|
static
|
|
void gSPDrawObjRect(const ObjCoordinates & _coords)
|
|
{
|
|
GraphicsDrawer & drawer = dwnd().getDrawer();
|
|
drawer.setDMAVerticesSize(4);
|
|
SPVertex * pVtx = drawer.getDMAVerticesData();
|
|
SPVertex & vtx0 = pVtx[0];
|
|
vtx0.x = _coords.ulx;
|
|
vtx0.y = _coords.uly;
|
|
vtx0.z = _coords.z;
|
|
vtx0.w = _coords.w;
|
|
vtx0.s = _coords.uls;
|
|
vtx0.t = _coords.ult;
|
|
SPVertex & vtx1 = pVtx[1];
|
|
vtx1.x = _coords.lrx;
|
|
vtx1.y = _coords.uly;
|
|
vtx1.z = _coords.z;
|
|
vtx1.w = _coords.w;
|
|
vtx1.s = _coords.lrs;
|
|
vtx1.t = _coords.ult;
|
|
SPVertex & vtx2 = pVtx[2];
|
|
vtx2.x = _coords.ulx;
|
|
vtx2.y = _coords.lry;
|
|
vtx2.z = _coords.z;
|
|
vtx2.w = _coords.w;
|
|
vtx2.s = _coords.uls;
|
|
vtx2.t = _coords.lrt;
|
|
SPVertex & vtx3 = pVtx[3];
|
|
vtx3.x = _coords.lrx;
|
|
vtx3.y = _coords.lry;
|
|
vtx3.z = _coords.z;
|
|
vtx3.w = _coords.w;
|
|
vtx3.s = _coords.lrs;
|
|
vtx3.t = _coords.lrt;
|
|
|
|
drawer.drawScreenSpaceTriangle(4);
|
|
}
|
|
|
|
static
|
|
void gSPSetSpriteTile(const uObjSprite *_pObjSprite)
|
|
{
|
|
const u32 w = std::max(_pObjSprite->imageW >> 5, 1);
|
|
const u32 h = std::max(_pObjSprite->imageH >> 5, 1);
|
|
|
|
gDPSetTile(_pObjSprite->imageFmt, _pObjSprite->imageSiz, _pObjSprite->imageStride, _pObjSprite->imageAdrs, 0, _pObjSprite->imagePal, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0);
|
|
gDPSetTileSize(0, 0, 0, (w - 1) << 2, (h - 1) << 2);
|
|
gSPTexture(1.0f, 1.0f, 0, 0, TRUE);
|
|
}
|
|
|
|
static
|
|
void gSPObjLoadTxtr(u32 tx)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(tx);
|
|
uObjTxtr *objTxtr = (uObjTxtr*)&RDRAM[address];
|
|
|
|
if ((gSP.status[objTxtr->block.sid >> 2] & objTxtr->block.mask) != objTxtr->block.flag) {
|
|
switch (objTxtr->block.type) {
|
|
case G_OBJLT_TXTRBLOCK:
|
|
gDPSetTextureImage(0, 1, 0, objTxtr->block.image);
|
|
gDPSetTile(0, 1, 0, objTxtr->block.tmem, 7, 0, 0, 0, 0, 0, 0, 0);
|
|
gDPLoadBlock(7, 0, 0, ((objTxtr->block.tsize + 1) << 3) - 1, objTxtr->block.tline);
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjLoadTxtr: load block\n");
|
|
break;
|
|
case G_OBJLT_TXTRTILE:
|
|
gDPSetTextureImage(0, 1, (objTxtr->tile.twidth + 1) << 1, objTxtr->tile.image);
|
|
gDPSetTile(0, 1, (objTxtr->tile.twidth + 1) >> 2, objTxtr->tile.tmem, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
gDPSetTile(0, 1, (objTxtr->tile.twidth + 1) >> 2, objTxtr->tile.tmem, 7, 0, 0, 0, 0, 0, 0, 0);
|
|
gDPLoadTile(7, 0, 0, (((objTxtr->tile.twidth + 1) << 1) - 1) << 2, (((objTxtr->tile.theight + 1) >> 2) - 1) << 2);
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjLoadTxtr: load tile\n");
|
|
break;
|
|
case G_OBJLT_TLUT:
|
|
gDPSetTextureImage(0, 2, 1, objTxtr->tlut.image);
|
|
gDPSetTile(0, 2, 0, objTxtr->tlut.phead, 7, 0, 0, 0, 0, 0, 0, 0);
|
|
gDPLoadTLUT(7, 0, 0, objTxtr->tlut.pnum << 2, 0);
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjLoadTxtr: load tlut\n");
|
|
break;
|
|
}
|
|
gSP.status[objTxtr->block.sid >> 2] = (gSP.status[objTxtr->block.sid >> 2] & ~objTxtr->block.mask) | (objTxtr->block.flag & objTxtr->block.mask);
|
|
}
|
|
}
|
|
|
|
static
|
|
void gSPObjRectangle(u32 _sp)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(_sp);
|
|
uObjSprite *objSprite = (uObjSprite*)&RDRAM[address];
|
|
gSPSetSpriteTile(objSprite);
|
|
|
|
ObjCoordinates objCoords(objSprite, false);
|
|
gSPDrawObjRect(objCoords);
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjRectangle\n");
|
|
}
|
|
|
|
static
|
|
void gSPObjRectangleR(u32 _sp)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(_sp);
|
|
const uObjSprite *objSprite = (uObjSprite*)&RDRAM[address];
|
|
gSPSetSpriteTile(objSprite);
|
|
ObjCoordinates objCoords(objSprite, true);
|
|
|
|
if (objSprite->imageFmt == G_IM_FMT_YUV && (config.generalEmulation.hacks&hack_Ogre64)) //Ogre Battle needs to copy YUV texture to frame buffer
|
|
_drawYUVImageToFrameBuffer(objCoords);
|
|
gSPDrawObjRect(objCoords);
|
|
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjRectangleR\n");
|
|
}
|
|
|
|
static
|
|
void gSPObjSprite(u32 _sp)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(_sp);
|
|
uObjSprite *objSprite = (uObjSprite*)&RDRAM[address];
|
|
gSPSetSpriteTile(objSprite);
|
|
|
|
/* Fixed point coordinates calculation. Decoded by olivieryuyu */
|
|
// X1 = AND (X + B3) by B0 + ((objX + A3) * A) >> 16 + ((objY + A3) * B) >> 16
|
|
// Y1 = AND (Y + B3) by B0 + ((objX + A3) * C) >> 16 + ((objY + A3) * D) >> 16
|
|
// X2 = AND (X + B3) by B0 + ((((imageW - A1) * 0x0100)/(scaleW * 2) + objX + A3) * A) >> 16 + ((((imageH - A1) * 0x0100)/(scaleH * 2) + objY + A3) * B) >> 16
|
|
// Y2 = AND (Y + B3) by B0 + ((((imageW - A1) * 0x0100)/(scaleW * 2) + objX + A3) * C) >> 16 + ((((imageH - A1) * 0x0100)/(scaleH * 2) + objY + A3) * D) >> 16
|
|
|
|
S2DEXCoordCorrector CC;
|
|
const s16 x0 = (objMtx.X + CC.B3) & CC.B0;
|
|
const s16 y0 = (objMtx.Y + CC.B3) & CC.B0;
|
|
const s16 ulx = objSprite->objX + CC.A3;
|
|
const s16 uly = objSprite->objY + CC.A3;
|
|
const s16 lrx = ((u32(objSprite->imageW) - CC.A1) << 8) / (u32(objSprite->scaleW) << 1) + ulx;
|
|
const s16 lry = ((u32(objSprite->imageH) - CC.A1) << 8) / (u32(objSprite->scaleH) << 1) + uly;
|
|
|
|
auto calcX = [&](s16 _x, s16 _y) -> f32
|
|
{
|
|
const s16 X = x0 + static_cast<s16>(((_x * objMtx.A) >> 16)) + static_cast<s16>(((_y * objMtx.B) >> 16));
|
|
return _FIXED2FLOAT(X, 2);
|
|
};
|
|
|
|
auto calcY = [&](s16 _x, s16 _y) -> f32
|
|
{
|
|
const s16 Y = y0 + static_cast<s16>(((_x * objMtx.C) >> 16)) + static_cast<s16>(((_y * objMtx.D) >> 16));
|
|
return _FIXED2FLOAT(Y, 2);
|
|
};
|
|
|
|
f32 uls = 0.0f;
|
|
f32 lrs = _FIXED2FLOAT(objSprite->imageW, 5) - 1.0f;
|
|
f32 ult = 0.0f;
|
|
f32 lrt = _FIXED2FLOAT(objSprite->imageH, 5) - 1.0f;
|
|
|
|
if (objSprite->imageFlags & G_BG_FLAG_FLIPS)
|
|
std::swap(uls, lrs);
|
|
|
|
if (objSprite->imageFlags & G_BG_FLAG_FLIPT)
|
|
std::swap(ult, lrt);
|
|
|
|
const float z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
|
|
|
|
GraphicsDrawer & drawer = dwnd().getDrawer();
|
|
drawer.setDMAVerticesSize(4);
|
|
SPVertex * pVtx = drawer.getDMAVerticesData();
|
|
|
|
SPVertex & vtx0 = pVtx[0];
|
|
vtx0.x = calcX(ulx, uly);
|
|
vtx0.y = calcY(ulx, uly);
|
|
vtx0.z = z;
|
|
vtx0.w = 1.0f;
|
|
vtx0.s = uls;
|
|
vtx0.t = ult;
|
|
SPVertex & vtx1 = pVtx[1];
|
|
vtx1.x = calcX(lrx, uly);
|
|
vtx1.y = calcY(lrx, uly);
|
|
vtx1.z = z;
|
|
vtx1.w = 1.0f;
|
|
vtx1.s = lrs;
|
|
vtx1.t = ult;
|
|
SPVertex & vtx2 = pVtx[2];
|
|
vtx2.x = calcX(ulx, lry);
|
|
vtx2.y = calcY(ulx, lry);
|
|
vtx2.z = z;
|
|
vtx2.w = 1.0f;
|
|
vtx2.s = uls;
|
|
vtx2.t = lrt;
|
|
SPVertex & vtx3 = pVtx[3];
|
|
vtx3.x = calcX(lrx, lry);
|
|
vtx3.y = calcY(lrx, lry);
|
|
vtx3.z = z;
|
|
vtx3.w = 1.0f;
|
|
vtx3.s = lrs;
|
|
vtx3.t = lrt;
|
|
|
|
drawer.drawScreenSpaceTriangle(4);
|
|
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjSprite\n");
|
|
}
|
|
|
|
static
|
|
void gSPObjMatrix(u32 mtx)
|
|
{
|
|
objMtx = *reinterpret_cast<const uObjMtx *>(RDRAM + RSP_SegmentToPhysical(mtx));
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjMatrix\n");
|
|
}
|
|
|
|
static
|
|
void gSPObjSubMatrix(u32 mtx)
|
|
{
|
|
const uObjSubMtx * pObjSubMtx = reinterpret_cast<const uObjSubMtx*>(RDRAM + RSP_SegmentToPhysical(mtx));
|
|
objMtx.X = pObjSubMtx->X;
|
|
objMtx.Y = pObjSubMtx->Y;
|
|
objMtx.BaseScaleX = pObjSubMtx->BaseScaleX;
|
|
objMtx.BaseScaleY = pObjSubMtx->BaseScaleY;
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjSubMatrix\n");
|
|
}
|
|
|
|
static
|
|
void _copyDepthBuffer()
|
|
{
|
|
if (!config.frameBufferEmulation.enable)
|
|
return;
|
|
|
|
if (!Context::BlitFramebuffer)
|
|
return;
|
|
|
|
// The game copies content of depth buffer into current color buffer
|
|
// OpenGL has different format for color and depth buffers, so this trick can't be performed directly
|
|
// To do that, depth buffer with address of current color buffer created and attached to the current FBO
|
|
// It will be copy depth buffer
|
|
DepthBufferList & dbList = depthBufferList();
|
|
dbList.saveBuffer(gDP.colorImage.address);
|
|
// Take any frame buffer and attach source depth buffer to it, to blit it into copy depth buffer
|
|
FrameBufferList & fbList = frameBufferList();
|
|
FrameBuffer * pTmpBuffer = fbList.findTmpBuffer(fbList.getCurrent()->m_startAddress);
|
|
if (pTmpBuffer == nullptr)
|
|
return;
|
|
DepthBuffer * pCopyBufferDepth = dbList.findBuffer(gSP.bgImage.address);
|
|
if (pCopyBufferDepth == nullptr)
|
|
return;
|
|
pCopyBufferDepth->setDepthAttachment(pTmpBuffer->m_FBO, bufferTarget::READ_FRAMEBUFFER);
|
|
|
|
DisplayWindow & wnd = dwnd();
|
|
Context::BlitFramebuffersParams blitParams;
|
|
blitParams.readBuffer = pTmpBuffer->m_FBO;
|
|
blitParams.drawBuffer = fbList.getCurrent()->m_FBO;
|
|
blitParams.srcX0 = 0;
|
|
blitParams.srcY0 = 0;
|
|
blitParams.srcX1 = wnd.getWidth();
|
|
blitParams.srcY1 = wnd.getHeight();
|
|
blitParams.dstX0 = 0;
|
|
blitParams.dstY0 = 0;
|
|
blitParams.dstX1 = wnd.getWidth();
|
|
blitParams.dstY1 = wnd.getHeight();
|
|
blitParams.mask = blitMask::DEPTH_BUFFER;
|
|
blitParams.filter = textureParameters::FILTER_NEAREST;
|
|
|
|
gfxContext.blitFramebuffers(blitParams);
|
|
|
|
// Restore objects
|
|
if (pTmpBuffer->m_pDepthBuffer != nullptr)
|
|
pTmpBuffer->m_pDepthBuffer->setDepthAttachment(fbList.getCurrent()->m_FBO, bufferTarget::READ_FRAMEBUFFER);
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
|
|
|
// Set back current depth buffer
|
|
dbList.saveBuffer(gDP.depthImageAddress);
|
|
}
|
|
|
|
static
|
|
void _loadBGImage(const uObjScaleBg * _bgInfo, bool _loadScale)
|
|
{
|
|
gSP.bgImage.address = RSP_SegmentToPhysical(_bgInfo->imagePtr);
|
|
|
|
const u32 imageW = _bgInfo->imageW >> 2;
|
|
const u32 imageH = _bgInfo->imageH >> 2;
|
|
if (imageW == 512 && (config.generalEmulation.hacks & hack_RE2) != 0) {
|
|
gSP.bgImage.width = *REG.VI_WIDTH;
|
|
gSP.bgImage.height = (imageH * imageW) / gSP.bgImage.width;
|
|
}
|
|
else {
|
|
gSP.bgImage.width = imageW - imageW % 2;
|
|
gSP.bgImage.height = imageH - imageH % 2;
|
|
}
|
|
gSP.bgImage.format = _bgInfo->imageFmt;
|
|
gSP.bgImage.size = _bgInfo->imageSiz;
|
|
gSP.bgImage.palette = _bgInfo->imagePal;
|
|
gDP.tiles[0].textureMode = TEXTUREMODE_BGIMAGE;
|
|
gSP.bgImage.imageX = _FIXED2FLOAT(_bgInfo->imageX, 5);
|
|
gSP.bgImage.imageY = _FIXED2FLOAT(_bgInfo->imageY, 5);
|
|
if (_loadScale) {
|
|
gSP.bgImage.scaleW = _FIXED2FLOAT(_bgInfo->scaleW, 10);
|
|
gSP.bgImage.scaleH = _FIXED2FLOAT(_bgInfo->scaleH, 10);
|
|
}
|
|
else
|
|
gSP.bgImage.scaleW = gSP.bgImage.scaleH = 1.0f;
|
|
|
|
if (config.frameBufferEmulation.enable) {
|
|
FrameBuffer *pBuffer = frameBufferList().findBuffer(gSP.bgImage.address);
|
|
if ((pBuffer != nullptr) && pBuffer->m_size == gSP.bgImage.size && (!pBuffer->m_isDepthBuffer || pBuffer->m_changed)) {
|
|
if (gSP.bgImage.format == G_IM_FMT_CI && gSP.bgImage.size == G_IM_SIZ_8b) {
|
|
// Can't use 8bit CI buffer as texture
|
|
return;
|
|
}
|
|
|
|
if (pBuffer->m_cfb || !pBuffer->isValid(false)) {
|
|
frameBufferList().removeBuffer(pBuffer->m_startAddress);
|
|
return;
|
|
}
|
|
|
|
gDP.tiles[0].frameBufferAddress = pBuffer->m_startAddress;
|
|
gDP.tiles[0].textureMode = TEXTUREMODE_FRAMEBUFFER_BG;
|
|
gDP.tiles[0].loadType = LOADTYPE_TILE;
|
|
gDP.changed |= CHANGED_TMEM;
|
|
|
|
if ((config.generalEmulation.hacks & hack_ZeldaMM) != 0) {
|
|
if (gDP.colorImage.address == gDP.depthImageAddress)
|
|
frameBufferList().setCopyBuffer(frameBufferList().getCurrent());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void gSPBgRect1Cyc(u32 _bg)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(_bg);
|
|
uObjScaleBg *objScaleBg = (uObjScaleBg*)&RDRAM[address];
|
|
_loadBGImage(objScaleBg, true);
|
|
|
|
// Zelda MM uses depth buffer copy in LoT and in pause screen.
|
|
// In later case depth buffer is used as temporal color buffer, and usual rendering must be used.
|
|
// Since both situations are hard to distinguish, do the both depth buffer copy and bg rendering.
|
|
if ((config.generalEmulation.hacks & hack_ZeldaMM) != 0 &&
|
|
(gSP.bgImage.address == gDP.depthImageAddress || depthBufferList().findBuffer(gSP.bgImage.address) != nullptr)
|
|
)
|
|
_copyDepthBuffer();
|
|
|
|
gDP.otherMode.cycleType = G_CYC_1CYCLE;
|
|
gDP.changed |= CHANGED_CYCLETYPE;
|
|
gSPTexture(1.0f, 1.0f, 0, 0, TRUE);
|
|
|
|
ObjCoordinates objCoords(objScaleBg);
|
|
gSPDrawObjRect(objCoords);
|
|
|
|
DebugMsg(DEBUG_NORMAL, "gSPBgRect1Cyc\n");
|
|
}
|
|
|
|
static
|
|
void gSPBgRectCopy(u32 _bg)
|
|
{
|
|
const u32 address = RSP_SegmentToPhysical(_bg);
|
|
uObjScaleBg *objBg = (uObjScaleBg*)&RDRAM[address];
|
|
_loadBGImage(objBg, false);
|
|
|
|
// See comment to gSPBgRect1Cyc
|
|
if ((config.generalEmulation.hacks & hack_ZeldaMM) != 0 &&
|
|
(gSP.bgImage.address == gDP.depthImageAddress || depthBufferList().findBuffer(gSP.bgImage.address) != nullptr)
|
|
)
|
|
_copyDepthBuffer();
|
|
|
|
gDP.otherMode.cycleType = G_CYC_COPY;
|
|
gDP.changed |= CHANGED_CYCLETYPE;
|
|
gSPTexture(1.0f, 1.0f, 0, 0, TRUE);
|
|
|
|
ObjCoordinates objCoords(objBg);
|
|
gSPDrawObjRect(objCoords);
|
|
|
|
DebugMsg(DEBUG_NORMAL, "gSPBgRectCopy\n");
|
|
}
|
|
|
|
void S2DEX_BG_1Cyc(u32 w0, u32 w1)
|
|
{
|
|
gSPBgRect1Cyc(w1);
|
|
}
|
|
|
|
void S2DEX_BG_Copy(u32 w0, u32 w1)
|
|
{
|
|
gSPBgRectCopy(w1);
|
|
}
|
|
|
|
void S2DEX_Obj_MoveMem(u32 w0, u32 w1)
|
|
{
|
|
switch (_SHIFTR(w0, 0, 16)) {
|
|
case S2DEX_MV_MATRIX:
|
|
gSPObjMatrix(w1);
|
|
break;
|
|
case S2DEX_MV_SUBMUTRIX:
|
|
gSPObjSubMatrix(w1);
|
|
break;
|
|
case S2DEX_MV_VIEWPORT:
|
|
gSPViewport(w1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void S2DEX_MoveWord(u32 w0, u32 w1)
|
|
{
|
|
switch (_SHIFTR(w0, 16, 8))
|
|
{
|
|
case G_MW_GENSTAT:
|
|
gSPSetStatus(_SHIFTR(w0, 0, 16), w1);
|
|
break;
|
|
default:
|
|
F3D_MoveWord(w0, w1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void S2DEX_RDPHalf_0(u32 w0, u32 w1) {
|
|
if (RSP.nextCmd == G_SELECT_DL) {
|
|
gSP.selectDL.addr = _SHIFTR(w0, 0, 16);
|
|
gSP.selectDL.sid = _SHIFTR(w0, 18, 8);
|
|
gSP.selectDL.flag = w1;
|
|
return;
|
|
}
|
|
if (RSP.nextCmd == G_RDPHALF_1) {
|
|
RDP_TexRect(w0, w1);
|
|
return;
|
|
}
|
|
assert(false);
|
|
}
|
|
|
|
void S2DEX_Select_DL(u32 w0, u32 w1)
|
|
{
|
|
gSP.selectDL.addr |= (_SHIFTR(w0, 0, 16)) << 16;
|
|
const u8 sid = gSP.selectDL.sid;
|
|
const u32 flag = gSP.selectDL.flag;
|
|
const u32 mask = w1;
|
|
if ((gSP.status[sid] & mask) == flag)
|
|
// Do nothing;
|
|
return;
|
|
|
|
gSP.status[sid] = (gSP.status[sid] & ~mask) | (flag & mask);
|
|
|
|
switch (_SHIFTR(w0, 16, 8))
|
|
{
|
|
case G_DL_PUSH:
|
|
gSPDisplayList(gSP.selectDL.addr);
|
|
break;
|
|
case G_DL_NOPUSH:
|
|
gSPBranchList(gSP.selectDL.addr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void S2DEX_Obj_RenderMode(u32 w0, u32 w1)
|
|
{
|
|
gSP.objRendermode = w1;
|
|
DebugMsg(DEBUG_NORMAL, "gSPObjRendermode(0x%08x)\n", gSP.objRendermode);
|
|
}
|
|
|
|
void S2DEX_Obj_Rectangle(u32 w0, u32 w1)
|
|
{
|
|
gSPObjRectangle(w1);
|
|
}
|
|
|
|
void S2DEX_Obj_Rectangle_R(u32 w0, u32 w1)
|
|
{
|
|
gSPObjRectangleR(w1);
|
|
}
|
|
|
|
void S2DEX_Obj_Sprite(u32 w0, u32 w1)
|
|
{
|
|
gSPObjSprite(w1);
|
|
}
|
|
|
|
void S2DEX_Obj_LoadTxtr(u32 w0, u32 w1)
|
|
{
|
|
gSPObjLoadTxtr(w1);
|
|
}
|
|
|
|
void S2DEX_Obj_LdTx_Rect(u32 w0, u32 w1)
|
|
{
|
|
gSPObjLoadTxtr(w1);
|
|
gSPObjRectangle(w1 + sizeof(uObjTxtr));
|
|
}
|
|
|
|
void S2DEX_Obj_LdTx_Rect_R(u32 w0, u32 w1)
|
|
{
|
|
gSPObjLoadTxtr(w1);
|
|
gSPObjRectangleR(w1 + sizeof(uObjTxtr));
|
|
}
|
|
|
|
void S2DEX_Obj_LdTx_Sprite(u32 w0, u32 w1)
|
|
{
|
|
gSPObjLoadTxtr(w1);
|
|
gSPObjSprite(w1 + sizeof(uObjTxtr));
|
|
}
|
|
|
|
void S2DEX_Init()
|
|
{
|
|
gSPSetupFunctions();
|
|
// Set GeometryMode flags
|
|
GBI_InitFlags( F3DEX );
|
|
resetObjMtx();
|
|
|
|
GBI.PCStackSize = 18;
|
|
gs_bVer1_3 = false;
|
|
|
|
// GBI Command Command Value Command Function
|
|
GBI_SetGBI( G_SPNOOP, F3D_SPNOOP, F3D_SPNoOp );
|
|
GBI_SetGBI( G_BG_1CYC, S2DEX_BG_1CYC, S2DEX_BG_1Cyc );
|
|
GBI_SetGBI( G_BG_COPY, S2DEX_BG_COPY, S2DEX_BG_Copy );
|
|
GBI_SetGBI( G_OBJ_RECTANGLE, S2DEX_OBJ_RECTANGLE, S2DEX_Obj_Rectangle );
|
|
GBI_SetGBI( G_OBJ_SPRITE, S2DEX_OBJ_SPRITE, S2DEX_Obj_Sprite );
|
|
GBI_SetGBI( G_OBJ_MOVEMEM, S2DEX_OBJ_MOVEMEM, S2DEX_Obj_MoveMem );
|
|
GBI_SetGBI( G_DL, F3D_DL, F3D_DList );
|
|
GBI_SetGBI( G_SELECT_DL, S2DEX_SELECT_DL, S2DEX_Select_DL );
|
|
GBI_SetGBI( G_OBJ_RENDERMODE, S2DEX_OBJ_RENDERMODE, S2DEX_Obj_RenderMode );
|
|
GBI_SetGBI( G_OBJ_RECTANGLE_R, S2DEX_OBJ_RECTANGLE_R, S2DEX_Obj_Rectangle_R );
|
|
GBI_SetGBI( G_OBJ_LOADTXTR, S2DEX_OBJ_LOADTXTR, S2DEX_Obj_LoadTxtr );
|
|
GBI_SetGBI( G_OBJ_LDTX_SPRITE, S2DEX_OBJ_LDTX_SPRITE, S2DEX_Obj_LdTx_Sprite );
|
|
GBI_SetGBI( G_OBJ_LDTX_RECT, S2DEX_OBJ_LDTX_RECT, S2DEX_Obj_LdTx_Rect );
|
|
GBI_SetGBI( G_OBJ_LDTX_RECT_R, S2DEX_OBJ_LDTX_RECT_R, S2DEX_Obj_LdTx_Rect_R );
|
|
GBI_SetGBI( G_MOVEWORD, F3D_MOVEWORD, S2DEX_MoveWord );
|
|
GBI_SetGBI( G_SETOTHERMODE_H, F3D_SETOTHERMODE_H, F3D_SetOtherMode_H );
|
|
GBI_SetGBI( G_SETOTHERMODE_L, F3D_SETOTHERMODE_L, F3D_SetOtherMode_L );
|
|
GBI_SetGBI( G_ENDDL, F3D_ENDDL, F3D_EndDL );
|
|
GBI_SetGBI( G_RDPHALF_0, S2DEX_RDPHALF_0, S2DEX_RDPHalf_0 );
|
|
GBI_SetGBI( G_RDPHALF_1, F3D_RDPHALF_1, F3D_RDPHalf_1 );
|
|
GBI_SetGBI( G_RDPHALF_2, F3D_RDPHALF_2, F3D_RDPHalf_2 );
|
|
GBI_SetGBI( G_LOAD_UCODE, S2DEX_LOAD_UCODE, F3DEX_Load_uCode );
|
|
}
|
|
|
|
void S2DEX_1_03_Init()
|
|
{
|
|
S2DEX_Init();
|
|
gs_bVer1_3 = true;
|
|
}
|