mirror of
https://github.com/PrimeDecomp/prime.git
synced 2025-08-15 16:59:06 +00:00
1133 lines
34 KiB
C++
1133 lines
34 KiB
C++
#include "Kyoto/Graphics/CGraphics.hpp"
|
|
|
|
#include "Kyoto/Basics/COsContext.hpp"
|
|
#include "Kyoto/Basics/CStopwatch.hpp"
|
|
#include "Kyoto/CFrameDelayedKiller.hpp"
|
|
#include "Kyoto/Graphics/CGX.hpp"
|
|
#include "Kyoto/Graphics/CGraphicsSys.hpp"
|
|
#include "Kyoto/Graphics/CTexture.hpp"
|
|
#include "Kyoto/Math/CRelAngle.hpp"
|
|
|
|
#include "dolphin/vi.h"
|
|
|
|
#include <string.h>
|
|
|
|
bool CGraphicsSys::mGraphicsInitialized;
|
|
static CStopwatch sFPSTimer;
|
|
static uchar sSpareFrameBuffer[640 * 448];
|
|
|
|
// clang-format off
|
|
CTevCombiners::CTevPass CGraphics::kEnvModulateConstColor(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_RegisterC0,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_RasterAlpha,
|
|
CTevCombiners::kAS_RegisterA0,
|
|
CTevCombiners::kAS_Zero
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvConstColor(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_RegisterC0
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_RegisterA0
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvModulate(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_TextureColor,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_RasterAlpha,
|
|
CTevCombiners::kAS_TextureAlpha,
|
|
CTevCombiners::kAS_Zero
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvDecal(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_TextureColor,
|
|
CTevCombiners::kCS_TextureAlpha,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_RasterAlpha
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvBlend(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_One,
|
|
CTevCombiners::kCS_TextureColor,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_TextureAlpha,
|
|
CTevCombiners::kAS_RasterAlpha,
|
|
CTevCombiners::kAS_Zero
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvReplace(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_TextureColor
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_TextureAlpha
|
|
)
|
|
);
|
|
static CTevCombiners::CTevPass kEnvBlendCTandCConCF(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_RegisterC0,
|
|
CTevCombiners::kCS_TextureColor,
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_RasterAlpha
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvModulateAlpha(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_RasterColor
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_TextureAlpha,
|
|
CTevCombiners::kAS_RasterAlpha,
|
|
CTevCombiners::kAS_Zero
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvModulateColor(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_TextureColor,
|
|
CTevCombiners::kCS_RasterColor,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Konst,
|
|
CTevCombiners::kAS_RasterAlpha,
|
|
CTevCombiners::kAS_Zero
|
|
)
|
|
);
|
|
CTevCombiners::CTevPass CGraphics::kEnvModulateColorByAlpha(
|
|
CTevCombiners::ColorPass(
|
|
CTevCombiners::kCS_Zero,
|
|
CTevCombiners::kCS_PreviousColor,
|
|
CTevCombiners::kCS_PreviousAlpha,
|
|
CTevCombiners::kCS_Zero
|
|
),
|
|
CTevCombiners::AlphaPass(
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_Zero,
|
|
CTevCombiners::kAS_PreviousAlpha
|
|
)
|
|
);
|
|
// clang-format on
|
|
|
|
CGraphics::CRenderState CGraphics::sRenderState;
|
|
VecPtr CGraphics::vtxBuffer;
|
|
VecPtr CGraphics::nrmBuffer;
|
|
Vec2Ptr CGraphics::txtBuffer0;
|
|
Vec2Ptr CGraphics::txtBuffer1;
|
|
uint* CGraphics::clrBuffer;
|
|
bool CGraphics::mJustReset;
|
|
ERglCullMode CGraphics::mCullMode;
|
|
int CGraphics::mNumLightsActive;
|
|
float CGraphics::mDepthNear;
|
|
VecPtr CGraphics::mpVtxBuffer;
|
|
VecPtr CGraphics::mpNrmBuffer;
|
|
Vec2Ptr CGraphics::mpTxtBuffer0;
|
|
Vec2Ptr CGraphics::mpTxtBuffer1;
|
|
uint* CGraphics::mpClrBuffer;
|
|
|
|
struct CGXLightParams {
|
|
int x0_;
|
|
int x4_;
|
|
int x8_;
|
|
int xc_;
|
|
int x10_;
|
|
|
|
CGXLightParams() : x0_(4), x4_(0), x8_(0), xc_(2), x10_(2) {}
|
|
};
|
|
CGXLightParams mLightParams[8];
|
|
|
|
struct {
|
|
Vec vtx;
|
|
Vec nrm;
|
|
Vec2 uv0;
|
|
Vec2 uv1;
|
|
uint color;
|
|
ushort textureUsed;
|
|
uchar streamFlags;
|
|
} vtxDescr;
|
|
|
|
CVector3f CGraphics::kDefaultPositionVector(0.f, 0.f, 0.f);
|
|
CVector3f CGraphics::kDefaultDirectionVector(0.f, 0.f, 0.f);
|
|
CGraphics::CProjectionState CGraphics::mProj(true, -1.f, 1.f, 1.f, -1.f, 1.f, 100.f);
|
|
CTransform4f CGraphics::mViewMatrix = CTransform4f::Identity();
|
|
CTransform4f CGraphics::mModelMatrix = CTransform4f::Identity();
|
|
CColor CGraphics::mClearColor = CColor::Black();
|
|
CVector3f CGraphics::mViewPoint(0.f, 0.f, 0.f);
|
|
GXLightObj CGraphics::mLightObj[8];
|
|
GXTexRegion CGraphics::mTexRegions[GX_MAX_TEXMAP];
|
|
GXTexRegion CGraphics::mTexRegionsCI[GX_MAX_TEXMAP / 2];
|
|
GXRenderModeObj CGraphics::mRenderModeObj;
|
|
Mtx CGraphics::mGXViewPointMatrix;
|
|
Mtx CGraphics::mGXModelMatrix;
|
|
Mtx CGraphics::mGxModelView;
|
|
Mtx CGraphics::mCameraMtx;
|
|
|
|
int CGraphics::mNumPrimitives;
|
|
int CGraphics::mFrameCounter;
|
|
float CGraphics::mFramesPerSecond;
|
|
float CGraphics::mLastFramesPerSecond;
|
|
int CGraphics::mNumBreakpointsWaiting;
|
|
int CGraphics::mFlippingState;
|
|
bool CGraphics::mLastFrameUsedAbove;
|
|
bool CGraphics::mInterruptLastFrameUsedAbove;
|
|
uchar CGraphics::mLightActive;
|
|
uchar CGraphics::mLightsWereOn;
|
|
void* CGraphics::mpFrameBuf1;
|
|
void* CGraphics::mpFrameBuf2;
|
|
void* CGraphics::mpCurrenFrameBuf;
|
|
int CGraphics::mSpareBufferSize;
|
|
void* CGraphics::mpSpareBuffer;
|
|
int CGraphics::mSpareBufferTexCacheSize;
|
|
GXTexRegionCallback CGraphics::mGXDefaultTexRegionCallback;
|
|
void* CGraphics::mpFifo;
|
|
GXFifoObj* CGraphics::mpFifoObj;
|
|
int CGraphics::mRenderTimings;
|
|
float CGraphics::mSecondsMod900;
|
|
CTimeProvider* CGraphics::mpExternalTimeProvider;
|
|
int lbl_805A9408;
|
|
int lbl_805A940C;
|
|
int lbl_805A9410;
|
|
|
|
CViewport CGraphics::mViewport = {0, 0, 640, 448, 320.f, 240.f};
|
|
ELightType CGraphics::mLightTypes[8] = {
|
|
kLT_Directional, kLT_Directional, kLT_Directional, kLT_Directional,
|
|
kLT_Directional, kLT_Directional, kLT_Directional, kLT_Directional,
|
|
};
|
|
|
|
const CTevCombiners::CTevPass& CGraphics::kEnvPassthru = CTevCombiners::kEnvPassthru;
|
|
bool CGraphics::mIsBeginSceneClearFb = true;
|
|
ERglEnum CGraphics::mDepthFunc = kE_LEqual;
|
|
ERglPrimitive CGraphics::mCurrentPrimitive = kP_Points;
|
|
float CGraphics::mDepthFar = 1.f;
|
|
u32 CGraphics::mClearDepthValue = GX_MAX_Z24;
|
|
bool CGraphics::mIsGXModelMatrixIdentity = true;
|
|
bool CGraphics::mFirstFrame = true;
|
|
bool CGraphics::mUseVideoFilter = true;
|
|
float CGraphics::mBrightness = 1.f;
|
|
// Vec2 CGraphics::mBrightnessRange = {0.f, 2.f};
|
|
|
|
bool CGraphics::Startup(const COsContext& osContext, uint fifoSize, void* fifoBase) {
|
|
mpFifo = fifoBase;
|
|
mpFifoObj = GXInit(fifoBase, fifoSize);
|
|
GXFifoObj fifoObj;
|
|
GXInitFifoBase(&fifoObj, mpFifo, fifoSize);
|
|
GXSetCPUFifo(&fifoObj);
|
|
GXSetGPFifo(&fifoObj);
|
|
GXInitFifoLimits(mpFifoObj, fifoSize - 0x4000, fifoSize - 0x10000);
|
|
GXSetCPUFifo(mpFifoObj);
|
|
GXSetGPFifo(mpFifoObj);
|
|
GXSetMisc(GX_MT_XF_FLUSH, 8);
|
|
GXSetDither(GX_FALSE);
|
|
CGX::ResetGXStates();
|
|
InitGraphicsVariables();
|
|
ConfigureFrameBuffer(osContext);
|
|
for (int i = 0; i < ARRAY_SIZE(mTexRegions); i++) {
|
|
GXInitTexCacheRegion(&mTexRegions[i], false, 0x8000 * i, GX_TEXCACHE_32K,
|
|
0x80000 + (0x8000 * i), GX_TEXCACHE_32K);
|
|
}
|
|
for (int i = 0; i < ARRAY_SIZE(mTexRegionsCI); i++) {
|
|
GXInitTexCacheRegion(&mTexRegionsCI[i], false, (8 + (2 * i)) << 0xF, GX_TEXCACHE_32K,
|
|
(9 + (2 * i)) << 0xF, GX_TEXCACHE_32K);
|
|
}
|
|
mGXDefaultTexRegionCallback = GXSetTexRegionCallback(TexRegionCallback);
|
|
mSpareBufferSize = ARRAY_SIZE(sSpareFrameBuffer);
|
|
mpSpareBuffer = sSpareFrameBuffer;
|
|
mSpareBufferTexCacheSize = 0x10000;
|
|
return true;
|
|
}
|
|
|
|
GXTexRegion* CGraphics::TexRegionCallback(const GXTexObj* obj, GXTexMapID id) {
|
|
static int nextTexRgn = 0;
|
|
static int nextTexRgnCI = 0;
|
|
if (id == GX_TEXMAP7) {
|
|
return &mTexRegions[0];
|
|
} else {
|
|
GXTexFmt fmt = GXGetTexObjFmt(obj);
|
|
if (fmt != GX_TF_C4 && fmt != GX_TF_C8 && fmt != GX_TF_C14X2) {
|
|
if (nextTexRgn == 0) {
|
|
++nextTexRgn;
|
|
}
|
|
return &mTexRegions[nextTexRgn++ & 7];
|
|
} else {
|
|
return &mTexRegionsCI[nextTexRgnCI++ & 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraphics::InitGraphicsVariables() {
|
|
for (int i = 0; i < ARRAY_SIZE(mLightTypes); ++i) {
|
|
mLightTypes[i] = kLT_Directional;
|
|
}
|
|
mLightActive = 0;
|
|
SetDepthWriteMode(false, mDepthFunc, false);
|
|
SetCullMode(kCM_None);
|
|
SetAmbientColor(CColor(0.2f, 0.2f, 0.2f, 1.f));
|
|
mIsGXModelMatrixIdentity = false;
|
|
SetIdentityViewPointMatrix();
|
|
SetIdentityModelMatrix();
|
|
SetViewport(0, 0, mViewport.mWidth, mViewport.mHeight);
|
|
SetPerspective(60.f,
|
|
static_cast< float >(mViewport.mWidth) / static_cast< float >(mViewport.mHeight),
|
|
mProj.GetNear(), mProj.GetFar());
|
|
SetCopyClear(mClearColor, 1.f);
|
|
const GXColor white = {0xFF, 0xFF, 0xFF, 0xFF};
|
|
CGX::SetChanMatColor(CGX::Channel0, white);
|
|
sRenderState.ResetFlushAll();
|
|
}
|
|
|
|
void CGraphics::Shutdown() { GXSetTexRegionCallback(mGXDefaultTexRegionCallback); }
|
|
|
|
void CGraphics::InitGraphicsDefaults() {
|
|
SetDepthRange(0.f, 1.f);
|
|
mIsGXModelMatrixIdentity = false;
|
|
SetModelMatrix(mModelMatrix);
|
|
SetViewPointMatrix(mViewMatrix);
|
|
SetDepthWriteMode(false, mDepthFunc, false);
|
|
SetCullMode(mCullMode);
|
|
SetViewport(mViewport.mLeft, mViewport.mTop, mViewport.mWidth, mViewport.mHeight);
|
|
FlushProjection();
|
|
CTevCombiners::Init();
|
|
DisableAllLights();
|
|
SetDefaultVtxAttrFmt();
|
|
}
|
|
|
|
void CGraphics::ConfigureFrameBuffer(const COsContext& osContext) {
|
|
mRenderModeObj = osContext.GetRenderModeObj();
|
|
mpFrameBuf1 = osContext.GetFramebuf1();
|
|
mpFrameBuf2 = osContext.GetFramebuf2();
|
|
VIConfigure(&mRenderModeObj);
|
|
VISetNextFrameBuffer(mpFrameBuf1);
|
|
mpCurrenFrameBuf = mpFrameBuf2;
|
|
GXSetViewport(0.f, 0.f, static_cast< float >(mRenderModeObj.fbWidth),
|
|
static_cast< float >(mRenderModeObj.efbHeight), 0.f, 1.f);
|
|
GXSetScissor(0, 0, mRenderModeObj.fbWidth, mRenderModeObj.efbHeight);
|
|
GXSetDispCopySrc(0, 0, mRenderModeObj.fbWidth, mRenderModeObj.efbHeight);
|
|
GXSetDispCopyDst(mRenderModeObj.fbWidth, mRenderModeObj.efbHeight);
|
|
GXSetDispCopyYScale(static_cast< float >(mRenderModeObj.xfbHeight) /
|
|
static_cast< float >(mRenderModeObj.efbHeight));
|
|
GXSetCopyFilter(mRenderModeObj.aa, mRenderModeObj.sample_pattern, GX_ENABLE,
|
|
mRenderModeObj.vfilter);
|
|
if (mRenderModeObj.aa) {
|
|
GXSetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
|
|
} else {
|
|
GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
|
}
|
|
GXSetDispCopyGamma(GX_GM_1_0);
|
|
GXCopyDisp(mpCurrenFrameBuf, true);
|
|
VIFlush();
|
|
VIWaitForRetrace();
|
|
VIWaitForRetrace();
|
|
mViewport.mWidth = mRenderModeObj.fbWidth;
|
|
mViewport.mHeight = mRenderModeObj.efbHeight;
|
|
InitGraphicsDefaults();
|
|
}
|
|
|
|
void CGraphics::EnableLight(ERglLight light) {
|
|
CGX::SetNumChans(1);
|
|
int lightsWereOn = mLightActive;
|
|
GXLightID lightId = static_cast< GXLightID >(1 << light);
|
|
if ((lightsWereOn & lightId) == GX_LIGHT_NULL) {
|
|
mLightActive |= lightId;
|
|
CGX::SetChanCtrl(CGX::Channel0, true, GX_SRC_REG, GX_SRC_REG,
|
|
static_cast< GXLightID >((lightsWereOn | lightId) & (GX_MAX_LIGHT - 1)),
|
|
GX_DF_CLAMP, GX_AF_SPOT);
|
|
++mNumLightsActive;
|
|
}
|
|
mLightsWereOn = mLightActive;
|
|
}
|
|
|
|
static inline GXLightID get_hw_light_index(ERglLight light) {
|
|
#if NONMATCHING
|
|
// one instruction, no branches
|
|
return static_cast< GXLightID >((light << 1) & (GX_MAX_LIGHT - 1));
|
|
#else
|
|
if (light == kLight0) {
|
|
return GX_LIGHT0;
|
|
} else if (light == kLight1) {
|
|
return GX_LIGHT1;
|
|
} else if (light == kLight2) {
|
|
return GX_LIGHT2;
|
|
} else if (light == kLight3) {
|
|
return GX_LIGHT3;
|
|
} else if (light == kLight4) {
|
|
return GX_LIGHT4;
|
|
} else if (light == kLight5) {
|
|
return GX_LIGHT5;
|
|
} else if (light == kLight6) {
|
|
return GX_LIGHT6;
|
|
}
|
|
// wtf?
|
|
return static_cast< GXLightID >(light == kLight7 ? GX_LIGHT7 : 0);
|
|
#endif
|
|
}
|
|
|
|
void CGraphics::LoadLight(ERglLight light, const CLight& info) {
|
|
GXLightID lightId = get_hw_light_index(light);
|
|
ELightType type = info.GetType();
|
|
CVector3f pos = info.GetPosition();
|
|
CVector3f dir = info.GetDirection();
|
|
|
|
switch (type) {
|
|
case kLT_Spot: {
|
|
MTXMultVec(mCameraMtx, reinterpret_cast< VecPtr >(&pos), reinterpret_cast< VecPtr >(&pos));
|
|
GXLightObj* obj = &mLightObj[light];
|
|
GXInitLightPos(obj, pos.GetX(), pos.GetY(), pos.GetZ());
|
|
MTXMultVecSR(mCameraMtx, reinterpret_cast< VecPtr >(&dir), reinterpret_cast< VecPtr >(&dir));
|
|
GXInitLightDir(obj, dir.GetX(), dir.GetY(), dir.GetZ());
|
|
GXInitLightAttn(obj, 1.f, 0.f, 0.f, info.GetAttenuationConstant(), info.GetAttenuationLinear(),
|
|
info.GetAttenuationQuadratic());
|
|
GXInitLightSpot(obj, info.GetSpotCutoff(), GX_SP_COS2);
|
|
break;
|
|
}
|
|
case kLT_Point:
|
|
case kLT_LocalAmbient: {
|
|
MTXMultVec(mCameraMtx, reinterpret_cast< VecPtr >(&pos), reinterpret_cast< VecPtr >(&pos));
|
|
GXInitLightPos(&mLightObj[light], pos.GetX(), pos.GetY(), pos.GetZ());
|
|
GXInitLightAttn(&mLightObj[light], 1.f, 0.f, 0.f, info.GetAttenuationConstant(),
|
|
info.GetAttenuationLinear(), info.GetAttenuationQuadratic());
|
|
break;
|
|
}
|
|
case kLT_Directional: {
|
|
MTXMultVecSR(mCameraMtx, reinterpret_cast< VecPtr >(&dir), reinterpret_cast< VecPtr >(&dir));
|
|
dir = -dir;
|
|
GXInitLightPos(&mLightObj[light], dir.GetX() * 1048576.f, dir.GetY() * 1048576.f,
|
|
dir.GetZ() * 1048576.f);
|
|
GXInitLightAttn(&mLightObj[light], 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
|
|
break;
|
|
}
|
|
case kLT_Custom: {
|
|
MTXMultVec(mCameraMtx, reinterpret_cast< VecPtr >(&pos), reinterpret_cast< VecPtr >(&pos));
|
|
GXLightObj* obj = &mLightObj[light];
|
|
GXInitLightPos(obj, pos.GetX(), pos.GetY(), pos.GetZ());
|
|
MTXMultVecSR(mCameraMtx, reinterpret_cast< VecPtr >(&dir), reinterpret_cast< VecPtr >(&dir));
|
|
GXInitLightDir(obj, dir.GetX(), dir.GetY(), dir.GetZ());
|
|
GXInitLightAttn(obj, info.GetAngleAttenuationConstant(), info.GetAngleAttenuationLinear(),
|
|
info.GetAngleAttenuationQuadratic(), info.GetAttenuationConstant(),
|
|
info.GetAttenuationLinear(), info.GetAttenuationQuadratic());
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
GXInitLightColor(&mLightObj[light], info.GetColor().GetGXColor());
|
|
GXLoadLightObjImm(&mLightObj[light], lightId);
|
|
mLightTypes[light] = info.GetType();
|
|
}
|
|
|
|
void CGraphics::DisableAllLights() {
|
|
mNumLightsActive = 0;
|
|
mLightActive = 0;
|
|
CGX::SetChanCtrl(CGX::Channel0, false, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE,
|
|
GX_AF_NONE);
|
|
}
|
|
|
|
// https://en.wikipedia.org/wiki/Hamming_weight
|
|
static inline uint popcount8(uint b) {
|
|
b = (b & 0x55) + ((b & 0xAA) >> 1);
|
|
b = (b & 0x33) + ((b & 0xCC) >> 2);
|
|
return (static_cast< uchar >(b) & 0xF) + (static_cast< uchar >(b) >> 4);
|
|
}
|
|
|
|
void CGraphics::SetLightState(uchar lights) {
|
|
GXAttnFn attnFn = GX_AF_NONE;
|
|
if (lights != 0) {
|
|
attnFn = GX_AF_SPOT;
|
|
}
|
|
GXDiffuseFn diffFn = GX_DF_NONE;
|
|
if (lights != 0) {
|
|
diffFn = GX_DF_CLAMP;
|
|
}
|
|
CGX::SetChanCtrl(CGX::Channel0, lights != 0 ? GX_ENABLE : GX_DISABLE, GX_SRC_REG,
|
|
(vtxDescr.streamFlags & 2) != 0 ? GX_SRC_VTX : GX_SRC_REG,
|
|
static_cast< GXLightID >(lights), diffFn, attnFn);
|
|
mLightActive = lights;
|
|
mNumLightsActive = popcount8(lights);
|
|
}
|
|
|
|
void CGraphics::SetViewMatrix() {
|
|
Mtx mtx;
|
|
MTXTrans(mtx, -mViewPoint.GetX(), -mViewPoint.GetY(), -mViewPoint.GetZ());
|
|
MTXConcat(mGXViewPointMatrix, mtx, mCameraMtx);
|
|
if (mIsGXModelMatrixIdentity) {
|
|
MTXCopy(mCameraMtx, mGxModelView);
|
|
} else {
|
|
MTXConcat(mCameraMtx, mGXModelMatrix, mGxModelView);
|
|
}
|
|
GXLoadPosMtxImm(mGxModelView, GX_PNMTX0);
|
|
|
|
Mtx nrmMtx;
|
|
MTXInvXpose(mGxModelView, nrmMtx);
|
|
GXLoadNrmMtxImm(nrmMtx, GX_PNMTX0);
|
|
}
|
|
|
|
void CGraphics::SetViewPointMatrix(const CTransform4f& xf) {
|
|
mViewMatrix = xf;
|
|
mGXViewPointMatrix[0][0] = xf.Get00();
|
|
mGXViewPointMatrix[0][1] = xf.Get10();
|
|
mGXViewPointMatrix[0][2] = xf.Get20();
|
|
mGXViewPointMatrix[0][3] = 0.f;
|
|
mGXViewPointMatrix[1][0] = xf.Get02();
|
|
mGXViewPointMatrix[1][1] = xf.Get12();
|
|
mGXViewPointMatrix[1][2] = xf.Get22();
|
|
mGXViewPointMatrix[1][3] = 0.f;
|
|
mGXViewPointMatrix[2][0] = -xf.Get01();
|
|
mGXViewPointMatrix[2][1] = -xf.Get11();
|
|
mGXViewPointMatrix[2][2] = -xf.Get21();
|
|
mGXViewPointMatrix[2][3] = 0.f;
|
|
mViewPoint = xf.GetTranslation();
|
|
SetViewMatrix();
|
|
}
|
|
|
|
void CGraphics::SetIdentityViewPointMatrix() {
|
|
mViewMatrix = CTransform4f::Identity();
|
|
MTXIdentity(mGXViewPointMatrix);
|
|
mGXViewPointMatrix[2][2] = 0.f;
|
|
mGXViewPointMatrix[1][1] = 0.f;
|
|
mGXViewPointMatrix[1][2] = 1.f;
|
|
mGXViewPointMatrix[2][1] = -1.f;
|
|
mViewPoint = CVector3f::Zero();
|
|
SetViewMatrix();
|
|
}
|
|
|
|
void CGraphics::SetModelMatrix(const CTransform4f& xf) {
|
|
if (&xf == &CTransform4f::Identity()) {
|
|
if (!mIsGXModelMatrixIdentity) {
|
|
mModelMatrix = xf;
|
|
mIsGXModelMatrixIdentity = true;
|
|
SetViewMatrix();
|
|
}
|
|
return;
|
|
}
|
|
|
|
mModelMatrix = xf;
|
|
mIsGXModelMatrixIdentity = false;
|
|
mGXModelMatrix[0][0] = xf.Get00();
|
|
mGXModelMatrix[0][1] = xf.Get01();
|
|
mGXModelMatrix[0][2] = xf.Get02();
|
|
mGXModelMatrix[0][3] = xf.Get03();
|
|
mGXModelMatrix[1][0] = xf.Get10();
|
|
mGXModelMatrix[1][1] = xf.Get11();
|
|
mGXModelMatrix[1][2] = xf.Get12();
|
|
mGXModelMatrix[1][3] = xf.Get13();
|
|
mGXModelMatrix[2][0] = xf.Get20();
|
|
mGXModelMatrix[2][1] = xf.Get21();
|
|
mGXModelMatrix[2][2] = xf.Get22();
|
|
mGXModelMatrix[2][3] = xf.Get23();
|
|
SetViewMatrix();
|
|
}
|
|
|
|
void CGraphics::SetIdentityModelMatrix() {
|
|
if (!mIsGXModelMatrixIdentity) {
|
|
mModelMatrix = CTransform4f::Identity();
|
|
mIsGXModelMatrixIdentity = true;
|
|
SetViewMatrix();
|
|
}
|
|
}
|
|
|
|
void CGraphics::SetOrtho(float left, float right, float top, float bottom, float znear,
|
|
float zfar) {
|
|
mProj = CProjectionState(false, left, right, top, bottom, znear, zfar);
|
|
FlushProjection();
|
|
}
|
|
|
|
// TODO non-matching (regswaps)
|
|
void CGraphics::SetPerspective(float fovy, float aspect, float znear, float zfar) {
|
|
float t = tan(CRelAngle::FromDegrees(fovy).AsRadians() / 2.f);
|
|
float top = znear * 2.f * t * 0.5f;
|
|
float right = aspect * 2.f * znear * t * 0.5f;
|
|
mProj = CProjectionState(true, -right, right, top, -top, znear, zfar);
|
|
FlushProjection();
|
|
}
|
|
|
|
CMatrix4f CGraphics::GetPerspectiveProjectionMatrix() {
|
|
#if NONMATCHING
|
|
// construct in place
|
|
return CMatrix4f(
|
|
#else
|
|
CMatrix4f mtx(
|
|
#endif
|
|
// clang-format off
|
|
(mProj.GetNear() * 2.f) / (mProj.GetRight() - mProj.GetLeft()),
|
|
-(mProj.GetRight() + mProj.GetLeft()) / (mProj.GetRight() - mProj.GetLeft()),
|
|
0.f,
|
|
0.f,
|
|
0.f,
|
|
-(mProj.GetTop() + mProj.GetBottom()) / (mProj.GetTop() - mProj.GetBottom()),
|
|
(mProj.GetNear() * 2.f) / (mProj.GetTop() - mProj.GetBottom()),
|
|
0.f,
|
|
0.f,
|
|
(mProj.GetFar() + mProj.GetNear()) / (mProj.GetFar() - mProj.GetNear()),
|
|
0.f,
|
|
-(mProj.GetFar() * 2.f * mProj.GetNear()) / (mProj.GetFar() - mProj.GetNear()),
|
|
0.f,
|
|
1.f,
|
|
0.f,
|
|
0.f
|
|
// clang-format on
|
|
);
|
|
#if !NONMATCHING
|
|
return mtx;
|
|
#endif
|
|
}
|
|
|
|
CMatrix4f CGraphics::CalculatePerspectiveMatrix(float fovy, float aspect, float znear, float zfar) {
|
|
float t = tan(CRelAngle::FromDegrees(fovy).AsRadians() / 2.f);
|
|
float right = aspect * 2.f * znear * t * 0.5f;
|
|
float left = -right;
|
|
float top = znear * 2.f * t * 0.5f;
|
|
float bottom = -top;
|
|
#if NONMATCHING
|
|
// construct in place
|
|
return CMatrix4f(
|
|
#else
|
|
CMatrix4f mtx(
|
|
#endif
|
|
// clang-format off
|
|
(2.f * znear) / (right - left),
|
|
-(right + left) / (right - left),
|
|
0.f,
|
|
0.f,
|
|
0.f,
|
|
-(top + bottom) / (top - bottom),
|
|
(2.f * znear) / (top - bottom),
|
|
0.f,
|
|
0.f,
|
|
(zfar + znear) / (zfar - znear),
|
|
0.f,
|
|
-(2.f * zfar * znear) / (zfar - znear),
|
|
0.f,
|
|
1.f,
|
|
0.f,
|
|
0.f
|
|
// clang-format on
|
|
);
|
|
#if !NONMATCHING
|
|
return mtx;
|
|
#endif
|
|
}
|
|
|
|
void CGraphics::SetViewport(int left, int bottom, int width, int height) {
|
|
mViewport.mLeft = left;
|
|
mViewport.mTop = mRenderModeObj.efbHeight - (bottom + height);
|
|
mViewport.mWidth = width;
|
|
mViewport.mHeight = height;
|
|
mViewport.mHalfWidth = static_cast< float >(width / 2);
|
|
mViewport.mHalfHeight = static_cast< float >(height / 2);
|
|
GXSetViewport(static_cast< float >(mViewport.mLeft), static_cast< float >(mViewport.mTop),
|
|
static_cast< float >(mViewport.mWidth), static_cast< float >(mViewport.mHeight),
|
|
mDepthNear, mDepthFar);
|
|
}
|
|
|
|
void CGraphics::SetScissor(int left, int bottom, int width, int height) {
|
|
GXSetScissor(left, mRenderModeObj.efbHeight - (bottom + height), width, height);
|
|
}
|
|
|
|
void CGraphics::SetAmbientColor(const CColor& color) {
|
|
CGX::SetChanAmbColor(CGX::Channel0, color.GetGXColor());
|
|
CGX::SetChanAmbColor(CGX::Channel1, color.GetGXColor());
|
|
}
|
|
|
|
void CGraphics::SetCopyClear(const CColor& color, float depth) {
|
|
mClearColor = color;
|
|
mClearDepthValue = static_cast< u32 >(depth * GX_MAX_Z24);
|
|
GXSetCopyClear(color.GetGXColor(), mClearDepthValue);
|
|
}
|
|
|
|
void CGraphics::SetClearColor(const CColor& color) {
|
|
mClearColor = color;
|
|
GXSetCopyClear(color.GetGXColor(), mClearDepthValue);
|
|
}
|
|
|
|
void CGraphics::ClearBackAndDepthBuffers() {
|
|
GXInvalidateTexAll();
|
|
if (mRenderModeObj.field_rendering) {
|
|
GXSetViewportJitter(0.f, 0.f, static_cast< float >(mRenderModeObj.fbWidth),
|
|
static_cast< float >(mRenderModeObj.xfbHeight), 0.f, 1.f, VIGetNextField());
|
|
} else {
|
|
GXSetViewport(0.f, 0.f, static_cast< float >(mRenderModeObj.fbWidth),
|
|
static_cast< float >(mRenderModeObj.xfbHeight), 0.f, 1.f);
|
|
}
|
|
GXInvalidateVtxCache();
|
|
}
|
|
|
|
void CGraphics::BeginScene() { ClearBackAndDepthBuffers(); }
|
|
|
|
void CGraphics::SwapBuffers() {
|
|
GXDisableBreakPt();
|
|
mFlippingState = 1;
|
|
}
|
|
|
|
void CGraphics::VideoPreCallback(u32 retraceCount) {
|
|
if (mNumBreakpointsWaiting != 0 && mFlippingState == 1) {
|
|
if (mFirstFrame) {
|
|
VISetBlack(GX_FALSE);
|
|
mFirstFrame = false;
|
|
}
|
|
VISetNextFrameBuffer(mpCurrenFrameBuf);
|
|
VIFlush();
|
|
void* frameBuf = mpCurrenFrameBuf == mpFrameBuf1 ? mpFrameBuf2 : mpFrameBuf1;
|
|
mFlippingState = 2;
|
|
mpCurrenFrameBuf = frameBuf;
|
|
}
|
|
}
|
|
|
|
void CGraphics::VideoPostCallback(u32 retraceCount) {
|
|
if (mNumBreakpointsWaiting != 0) {
|
|
if (mFlippingState == 2) {
|
|
--mNumBreakpointsWaiting;
|
|
mFlippingState = 0;
|
|
CStopwatch& timer = sFPSTimer;
|
|
float elapsed = timer.GetElapsedTime();
|
|
mLastFramesPerSecond = mFramesPerSecond;
|
|
mFramesPerSecond = 1.f / elapsed;
|
|
timer.Reset();
|
|
mInterruptLastFrameUsedAbove = VIGetNextField() == 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraphics::EndScene() {
|
|
CGX::SetZMode(true, GX_LEQUAL, true);
|
|
volatile int& numBreakPt = const_cast< volatile int& >(mNumBreakpointsWaiting);
|
|
while (numBreakPt > 0) {
|
|
OSYieldThread();
|
|
}
|
|
++mNumBreakpointsWaiting;
|
|
void*& frameBuf = mpCurrenFrameBuf;
|
|
float brightness = CMath::Clamp(0.f, mBrightness, 2.f);
|
|
static const u8 copyFilter[7] = {0x00, 0x00, 0x15, 0x16, 0x15, 0x00, 0x00};
|
|
const u8* inFilter = mUseVideoFilter ? mRenderModeObj.vfilter : copyFilter;
|
|
u8 vfilter[7];
|
|
for (int i = 0; i < 7; i++) {
|
|
vfilter[i] = static_cast< u8 >(static_cast< float >(inFilter[i]) * brightness);
|
|
}
|
|
GXSetCopyFilter(mRenderModeObj.aa, mRenderModeObj.sample_pattern, true, vfilter);
|
|
GXCopyDisp(frameBuf, mIsBeginSceneClearFb ? GX_TRUE : GX_FALSE);
|
|
GXSetCopyFilter(mRenderModeObj.aa, mRenderModeObj.sample_pattern,
|
|
mUseVideoFilter ? GX_ENABLE : GX_DISABLE, mRenderModeObj.vfilter);
|
|
GXSetBreakPtCallback(SwapBuffers);
|
|
VISetPreRetraceCallback(VideoPreCallback);
|
|
VISetPostRetraceCallback(VideoPostCallback);
|
|
GXFlush();
|
|
GXFifoObj* fifo = GXGetGPFifo();
|
|
void* readPtr;
|
|
void* writePtr;
|
|
GXGetFifoPtrs(fifo, &readPtr, &writePtr);
|
|
GXEnableBreakPt(writePtr);
|
|
mLastFrameUsedAbove = mInterruptLastFrameUsedAbove;
|
|
++mFrameCounter;
|
|
CFrameDelayedKiller::sub_8036cb90();
|
|
}
|
|
|
|
void CGraphics::SetDepthWriteMode(bool test, ERglEnum comp, bool write) {
|
|
mDepthFunc = comp;
|
|
CGX::SetZMode(static_cast< uchar >(test), static_cast< GXCompare >(comp),
|
|
static_cast< uchar >(write));
|
|
}
|
|
|
|
void CGraphics::SetCullMode(ERglCullMode cullMode) {
|
|
mCullMode = cullMode;
|
|
GXSetCullMode(static_cast< GXCullMode >(cullMode));
|
|
}
|
|
|
|
void CGraphics::SetBlendMode(ERglBlendMode mode, ERglBlendFactor src, ERglBlendFactor dst,
|
|
ERglLogicOp op) {
|
|
CGX::SetBlendMode(static_cast< GXBlendMode >(mode), static_cast< GXBlendFactor >(src),
|
|
static_cast< GXBlendFactor >(dst), static_cast< GXLogicOp >(op));
|
|
}
|
|
|
|
void CGraphics::SetAlphaCompare(ERglAlphaFunc comp0, uchar ref0, ERglAlphaOp op,
|
|
ERglAlphaFunc comp1, uchar ref1) {
|
|
CGX::SetAlphaCompare(static_cast< GXCompare >(comp0), static_cast< uchar >(ref0),
|
|
static_cast< GXAlphaOp >(op), static_cast< GXCompare >(comp1),
|
|
static_cast< uchar >(ref1));
|
|
}
|
|
|
|
static const GXVtxDescList skPosColorTexDirect[] = {
|
|
{GX_VA_POS, GX_DIRECT},
|
|
{GX_VA_CLR0, GX_DIRECT},
|
|
{GX_VA_TEX0, GX_DIRECT},
|
|
{GX_VA_NULL, GX_DIRECT},
|
|
};
|
|
|
|
// TODO non-matching (regswaps)
|
|
void CGraphics::Render2D(const CTexture& tex, int x, int y, int w, int h, const CColor& col) {
|
|
Mtx44 proj;
|
|
MTXOrtho(proj, mViewport.mHeight / 2, -mViewport.mHeight / 2, -mViewport.mWidth / 2,
|
|
mViewport.mWidth / 2, -1.f, -10.f);
|
|
GXSetProjection(proj, GX_ORTHOGRAPHIC);
|
|
uint c = col.GetColor_u32();
|
|
|
|
Mtx mtx;
|
|
MTXIdentity(mtx);
|
|
GXLoadPosMtxImm(mtx, GX_PNMTX0);
|
|
|
|
float x1 = x - mViewport.mWidth / 2;
|
|
float y1 = y - mViewport.mHeight / 2;
|
|
float x2 = x1 + w;
|
|
float y2 = y1 + h;
|
|
|
|
// Save state + setup
|
|
CGX::SetVtxDescv(skPosColorTexDirect);
|
|
SetTevStates(6);
|
|
mLightsWereOn = mLightActive;
|
|
if (mLightActive != 0) {
|
|
DisableAllLights();
|
|
}
|
|
ERglCullMode cullMode = mCullMode;
|
|
SetCullMode(kCM_None);
|
|
tex.Load(GX_TEXMAP0, CTexture::kCM_Repeat);
|
|
|
|
// Draw
|
|
CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4);
|
|
GXPosition3f32(x1, y1, 1.f);
|
|
GXColor1u32(c);
|
|
GXTexCoord2f32(0.f, 0.f);
|
|
GXPosition3f32(x2, y1, 1.f);
|
|
GXColor1u32(c);
|
|
GXTexCoord2f32(1.f, 0.f);
|
|
GXPosition3f32(x1, y2, 1.f);
|
|
GXColor1u32(c);
|
|
GXTexCoord2f32(0.f, 1.f);
|
|
GXPosition3f32(x2, y2, 1.f);
|
|
GXColor1u32(c);
|
|
GXTexCoord2f32(1.f, 1.f);
|
|
CGX::End();
|
|
|
|
// Restore state
|
|
if (mLightsWereOn != 0) {
|
|
SetLightState(mLightsWereOn);
|
|
}
|
|
FlushProjection();
|
|
mIsGXModelMatrixIdentity = false;
|
|
SetModelMatrix(mModelMatrix);
|
|
SetCullMode(cullMode);
|
|
}
|
|
|
|
void CGraphics::DrawPrimitive(ERglPrimitive primitive, const float* pos, const CVector3f& normal,
|
|
const CColor& col, int numVerts) {
|
|
StreamBegin(primitive);
|
|
StreamNormal(reinterpret_cast< const float* >(&normal));
|
|
StreamColor(col);
|
|
for (int i = 0; i < numVerts; ++i) {
|
|
StreamVertex(pos + i * 3);
|
|
}
|
|
StreamEnd();
|
|
}
|
|
|
|
#define STREAM_PRIM_BUFFER_SIZE 240
|
|
|
|
#define VTX_BUFFER_ADDR LC_BASE
|
|
#if NONMATCHING
|
|
// Bug fix: these should be 3 times larger to avoid overflow
|
|
// Likely the result of bad pointer arithmetic
|
|
#define NRM_BUFFER_ADDR (VTX_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(Vec)))
|
|
#define TXT0_BUFFER_ADDR (NRM_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(Vec)))
|
|
#else
|
|
#define NRM_BUFFER_ADDR (VTX_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(float)))
|
|
#define TXT0_BUFFER_ADDR (NRM_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(float)))
|
|
#endif
|
|
#define TXT1_BUFFER_ADDR (TXT0_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(Vec2)))
|
|
#define CLR_BUFFER_ADDR (TXT1_BUFFER_ADDR + ((STREAM_PRIM_BUFFER_SIZE + 1) * sizeof(Vec2)))
|
|
|
|
static const uchar kHasNormals = 1;
|
|
static const uchar kHasColor = 2;
|
|
static const uchar kHasTexture = 4;
|
|
|
|
void CGraphics::StreamBegin(ERglPrimitive primitive) {
|
|
vtxBuffer = reinterpret_cast< VecPtr >(VTX_BUFFER_ADDR);
|
|
nrmBuffer = reinterpret_cast< VecPtr >(NRM_BUFFER_ADDR);
|
|
txtBuffer0 = reinterpret_cast< Vec2Ptr >(TXT0_BUFFER_ADDR);
|
|
txtBuffer1 = reinterpret_cast< Vec2Ptr >(TXT1_BUFFER_ADDR);
|
|
clrBuffer = reinterpret_cast< uint* >(CLR_BUFFER_ADDR);
|
|
ResetVertexDataStream(true);
|
|
mCurrentPrimitive = primitive;
|
|
vtxDescr.streamFlags = kHasColor;
|
|
}
|
|
|
|
void CGraphics::StreamVertex(float x, float y, float z) {
|
|
vtxDescr.vtx.x = x;
|
|
vtxDescr.vtx.y = y;
|
|
vtxDescr.vtx.z = z;
|
|
UpdateVertexDataStream();
|
|
}
|
|
|
|
void CGraphics::StreamVertex(const float* vtx) {
|
|
vtxDescr.vtx.x = vtx[0];
|
|
vtxDescr.vtx.y = vtx[1];
|
|
vtxDescr.vtx.z = vtx[2];
|
|
UpdateVertexDataStream();
|
|
}
|
|
|
|
void CGraphics::StreamVertex(const CVector3f& vtx) {
|
|
vtxDescr.vtx.x = vtx.GetX();
|
|
vtxDescr.vtx.y = vtx.GetY();
|
|
vtxDescr.vtx.z = vtx.GetZ();
|
|
UpdateVertexDataStream();
|
|
}
|
|
|
|
void CGraphics::StreamNormal(const float* nrm) {
|
|
vtxDescr.nrm.x = nrm[0];
|
|
vtxDescr.nrm.y = nrm[1];
|
|
vtxDescr.nrm.z = nrm[2];
|
|
vtxDescr.streamFlags |= kHasNormals;
|
|
}
|
|
|
|
void CGraphics::StreamColor(uint color) {
|
|
vtxDescr.color = color;
|
|
vtxDescr.streamFlags |= kHasColor;
|
|
}
|
|
|
|
void CGraphics::StreamColor(const CColor& color) {
|
|
vtxDescr.color = color.GetColor_u32();
|
|
vtxDescr.streamFlags |= kHasColor;
|
|
}
|
|
|
|
void CGraphics::StreamColor(float r, float g, float b, float a) {
|
|
// clang-format off
|
|
vtxDescr.color = (static_cast< uchar >(r * 255.f) << 24) |
|
|
(static_cast< uchar >(g * 255.f) << 16) |
|
|
(static_cast< uchar >(b * 255.f) << 8) |
|
|
static_cast< uchar >(a * 255.f);
|
|
// clang-format on
|
|
vtxDescr.streamFlags |= kHasColor;
|
|
}
|
|
|
|
void CGraphics::StreamTexcoord(const CVector2f& uv) {
|
|
vtxDescr.uv0.x = uv.GetX();
|
|
vtxDescr.uv0.y = uv.GetY();
|
|
vtxDescr.streamFlags |= kHasTexture;
|
|
vtxDescr.textureUsed |= 1;
|
|
}
|
|
|
|
void CGraphics::StreamTexcoord(float u, float v) {
|
|
vtxDescr.uv0.x = u;
|
|
vtxDescr.uv0.y = v;
|
|
vtxDescr.streamFlags |= kHasTexture;
|
|
vtxDescr.textureUsed |= 1;
|
|
}
|
|
|
|
void CGraphics::StreamEnd() {
|
|
if (mNumPrimitives != 0) {
|
|
FlushStream();
|
|
}
|
|
vtxBuffer = nullptr;
|
|
vtxDescr.streamFlags = 0;
|
|
vtxDescr.textureUsed = 0;
|
|
nrmBuffer = nullptr;
|
|
txtBuffer0 = nullptr;
|
|
txtBuffer1 = nullptr;
|
|
clrBuffer = nullptr;
|
|
}
|
|
|
|
void CGraphics::SetLineWidth(float w, ERglTexOffset offs) {
|
|
CGX::SetLineWidth(static_cast< uchar >(w * 6.f), static_cast< GXTexOffset >(offs));
|
|
}
|
|
|
|
void CGraphics::UpdateVertexDataStream() {
|
|
++mNumPrimitives;
|
|
mpVtxBuffer->x = vtxDescr.vtx.x;
|
|
mpVtxBuffer->y = vtxDescr.vtx.y;
|
|
mpVtxBuffer->z = vtxDescr.vtx.z;
|
|
++mpVtxBuffer;
|
|
if ((vtxDescr.streamFlags & kHasNormals) != 0) {
|
|
mpNrmBuffer->x = vtxDescr.nrm.x;
|
|
mpNrmBuffer->y = vtxDescr.nrm.y;
|
|
mpNrmBuffer->z = vtxDescr.nrm.z;
|
|
++mpNrmBuffer;
|
|
}
|
|
if ((vtxDescr.streamFlags & kHasTexture) != 0) {
|
|
mpTxtBuffer0->x = vtxDescr.uv0.x;
|
|
mpTxtBuffer0->y = vtxDescr.uv0.y;
|
|
++mpTxtBuffer0;
|
|
|
|
mpTxtBuffer1->x = vtxDescr.uv1.x;
|
|
mpTxtBuffer1->y = vtxDescr.uv1.y;
|
|
++mpTxtBuffer1;
|
|
}
|
|
if ((vtxDescr.streamFlags & kHasColor) != 0) {
|
|
*mpClrBuffer = vtxDescr.color;
|
|
++mpClrBuffer;
|
|
}
|
|
mJustReset = 0;
|
|
if (mNumPrimitives == STREAM_PRIM_BUFFER_SIZE) {
|
|
FlushStream();
|
|
ResetVertexDataStream(false);
|
|
}
|
|
}
|
|
|
|
void CGraphics::ResetVertexDataStream(bool initial) {
|
|
mpVtxBuffer = vtxBuffer;
|
|
mpNrmBuffer = nrmBuffer;
|
|
mpTxtBuffer0 = txtBuffer0;
|
|
mpTxtBuffer1 = txtBuffer1;
|
|
mpClrBuffer = clrBuffer;
|
|
mNumPrimitives = 0;
|
|
|
|
if (initial) {
|
|
return;
|
|
}
|
|
|
|
switch (mCurrentPrimitive) {
|
|
case kP_TriangleFan:
|
|
mpVtxBuffer = vtxBuffer + 1;
|
|
memcpy(mpVtxBuffer, &vtxDescr.vtx, sizeof(Vec));
|
|
++mpVtxBuffer;
|
|
|
|
if ((vtxDescr.streamFlags & kHasNormals) != 0) {
|
|
++mpNrmBuffer;
|
|
memcpy(mpNrmBuffer, &vtxDescr.nrm, sizeof(Vec));
|
|
++mpNrmBuffer;
|
|
}
|
|
|
|
if ((vtxDescr.streamFlags & kHasTexture) != 0) {
|
|
++mpTxtBuffer0;
|
|
memcpy(mpTxtBuffer0, &vtxDescr.uv0, sizeof(Vec2));
|
|
++mpTxtBuffer0;
|
|
|
|
++mpTxtBuffer1;
|
|
memcpy(mpTxtBuffer1, &vtxDescr.uv1, sizeof(Vec2));
|
|
++mpTxtBuffer1;
|
|
}
|
|
|
|
if ((vtxDescr.streamFlags & kHasColor) != 0) {
|
|
++mpClrBuffer;
|
|
*mpClrBuffer = vtxDescr.color;
|
|
++mpClrBuffer;
|
|
}
|
|
|
|
mNumPrimitives += 2;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mJustReset = 1;
|
|
}
|
|
|
|
void CGraphics::FlushStream() {
|
|
GXVtxDescList vtxDesc[10];
|
|
|
|
GXVtxDescList* curDesc = vtxDesc;
|
|
const GXVtxDescList vtxDescPos = {GX_VA_POS, GX_DIRECT};
|
|
*curDesc++ = vtxDescPos;
|
|
|
|
if ((vtxDescr.streamFlags & kHasNormals) != 0) {
|
|
const GXVtxDescList vtxDescNrm = {GX_VA_CLR0, GX_DIRECT};
|
|
*curDesc++ = vtxDescNrm;
|
|
}
|
|
|
|
if ((vtxDescr.streamFlags & kHasColor) != 0) {
|
|
const GXVtxDescList vtxDescClr0 = {GX_VA_CLR0, GX_DIRECT};
|
|
*curDesc++ = vtxDescClr0;
|
|
}
|
|
|
|
if ((vtxDescr.streamFlags & kHasTexture) != 0) {
|
|
const GXVtxDescList vtxDescTex0 = {GX_VA_TEX0, GX_DIRECT};
|
|
*curDesc++ = vtxDescTex0;
|
|
}
|
|
|
|
const GXVtxDescList vtxDescNull = {GX_VA_NULL, GX_NONE};
|
|
*curDesc = vtxDescNull;
|
|
|
|
CGX::SetVtxDescv(vtxDesc);
|
|
SetTevStates(vtxDescr.streamFlags);
|
|
FullRender();
|
|
}
|
|
|
|
void CGraphics::SetTevStates(uchar flags) {
|
|
switch (flags) {
|
|
case 0:
|
|
case kHasNormals:
|
|
case kHasColor:
|
|
case kHasNormals | kHasColor:
|
|
CGX::SetNumChans(1);
|
|
CGX::SetNumTexGens(0);
|
|
CGX::SetNumTevStages(1);
|
|
CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
|
CGX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
|
break;
|
|
case kHasTexture:
|
|
case kHasNormals | kHasTexture:
|
|
case kHasColor | kHasTexture:
|
|
case kHasNormals | kHasColor | kHasTexture:
|
|
CGX::SetNumChans(1);
|
|
if ((vtxDescr.textureUsed & 3) != 0) {
|
|
CGX::SetNumTexGens(2);
|
|
} else {
|
|
CGX::SetNumTexGens(1);
|
|
}
|
|
CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
|
CGX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
|
|
break;
|
|
}
|
|
CGX::SetNumIndStages(0);
|
|
CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY, false, GX_PTIDENTITY);
|
|
CGX::SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, GX_IDENTITY, false, GX_PTIDENTITY);
|
|
|
|
uint light = mLightActive;
|
|
GXAttnFn attnFn = GX_AF_NONE;
|
|
if (light != 0) {
|
|
attnFn = GX_AF_SPOT;
|
|
}
|
|
GXDiffuseFn diffFn = GX_DF_NONE;
|
|
if (light != 0) {
|
|
diffFn = GX_DF_CLAMP;
|
|
}
|
|
CGX::SetChanCtrl(CGX::Channel0, light ? GX_ENABLE : GX_DISABLE, GX_SRC_REG,
|
|
(flags & kHasColor) ? GX_SRC_VTX : GX_SRC_REG, static_cast< GXLightID >(light),
|
|
diffFn, attnFn);
|
|
}
|