diff --git a/Runtime/Graphics/CCubeMaterial.cpp b/Runtime/Graphics/CCubeMaterial.cpp index c2ba601c1..87535da6f 100644 --- a/Runtime/Graphics/CCubeMaterial.cpp +++ b/Runtime/Graphics/CCubeMaterial.cpp @@ -162,24 +162,24 @@ void CCubeMaterial::SetCurrent(const CModelFlags& flags, const CCubeSurface& sur u32 fullTcgCount = SBig(*tcgs); tcgCount = std::min(fullTcgCount, 2u); for (u32 i = 0; i < tcgCount; ++i) { - // TODO set TCG + CGX::SetTexCoordGen(GX::TexCoordID(i), SBig(tcgs[i + 1])); } tcgs += fullTcgCount + 1; } else { tcgCount = SBig(*tcgs); for (u32 i = 0; i < tcgCount; ++i) { - // TODO set TCG + CGX::SetTexCoordGen(GX::TexCoordID(i), SBig(tcgs[i + 1])); } tcgs += tcgCount + 1; } const u32* uvAnim = tcgs; - u32 animCount = uvAnim[1]; + u32 animCount = SBig(uvAnim[1]); uvAnim += 2; - u32 texMtx = 30; - u32 pttTexMtx = 64; + u32 texMtx = GX::TEXMTX0; + u32 pttTexMtx = GX::PTTEXMTX0; for (u32 i = 0; i < animCount; ++i) { - u32 size = HandleAnimatedUV(uvAnim, texMtx, pttTexMtx); + u32 size = HandleAnimatedUV(uvAnim, static_cast(texMtx), static_cast(pttTexMtx)); if (size == 0) break; uvAnim += size; @@ -356,31 +356,73 @@ void CCubeMaterial::HandleTev(u32 tevCur, const u32* materialDataCur, const u32* CGX::SetTevKAlphaSel(stage, static_cast(matFlags >> 0x10 & 0xFF)); } -u32 CCubeMaterial::HandleAnimatedUV(const u32* uvAnim, u32 texMtx, u32 pttTexMtx) { +constexpr zeus::CTransform MvPostXf{ + {zeus::CVector3f{0.5f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.5f, 0.f}}, + {0.5f, 0.5f, 1.f}, +}; + +u32 CCubeMaterial::HandleAnimatedUV(const u32* uvAnim, GX::TexMtx texMtx, GX::PTTexMtx pttTexMtx) { u32 type = SBig(*uvAnim); + const float* params = reinterpret_cast(uvAnim + 1); switch (type) { - case 0: - // TODO + case 0: { + auto xf = CGraphics::g_GXModelViewInvXpose; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); + GXLoadTexMtxImm(&MvPostXf, pttTexMtx, GX::MTX3x4); return 1; - case 1: - // TODO + } + case 1: { + auto xf = CGraphics::g_GXModelViewInvXpose; + xf.origin = CGraphics::g_ViewMatrix.inverse() * CGraphics::g_GXModelMatrix.origin; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); + GXLoadTexMtxImm(&MvPostXf, pttTexMtx, GX::MTX3x4); return 1; - case 2: - // TODO + } + case 2: { + const float f1 = SBig(params[0]); + const float f2 = SBig(params[1]); + const float f3 = SBig(params[2]); + const float f4 = SBig(params[3]); + const float seconds = CGraphics::GetSecondsMod900(); + const auto xf = zeus::CTransform::Translate(seconds * f3 + f1, seconds * f4 + f2, 0.f); + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); return 5; - case 3: - // TODO + } + case 3: { + const float angle = CGraphics::GetSecondsMod900() * SBig(params[1]) + SBig(params[0]); + const float acos = std::cos(angle); + const float asin = std::sin(angle); + zeus::CTransform xf; + xf.basis[0][0] = acos; + xf.basis[0][1] = asin; + xf.basis[1][0] = -asin; + xf.basis[1][1] = acos; + xf.origin[0] = (1.f - (acos - asin)) * 0.5f; + xf.origin[1] = (1.f - (asin + acos)) * 0.5f; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); return 3; + } case 4: - case 5: + case 5: { // TODO + zeus::CTransform xf; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); return 5; - case 6: + } + case 6: { // TODO + zeus::CTransform xf; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); + GXLoadTexMtxImm(&xf, pttTexMtx, GX::MTX3x4); return 1; - case 7: + } + case 7: { // TODO - return 2; + zeus::CTransform xf; + GXLoadTexMtxImm(&xf, texMtx, GX::MTX3x4); + GXLoadTexMtxImm(&xf, pttTexMtx, GX::MTX3x4); + return 3; + } default: return 0; } diff --git a/Runtime/Graphics/CCubeMaterial.hpp b/Runtime/Graphics/CCubeMaterial.hpp index bc4492ea9..870f63dba 100644 --- a/Runtime/Graphics/CCubeMaterial.hpp +++ b/Runtime/Graphics/CCubeMaterial.hpp @@ -93,7 +93,7 @@ private: static void HandleDepth(CModelFlagsFlags modelFlags, CCubeMaterialFlags matFlags); static u32 HandleColorChannels(u32 chanCount, u32 firstChan); static void HandleTev(u32 tevCur, const u32* materialDataCur, const u32* texMapTexCoordFlags, bool shadowMapsEnabled); - static u32 HandleAnimatedUV(const u32* uvAnim, u32 texMtx, u32 pttTexMtx); + static u32 HandleAnimatedUV(const u32* uvAnim, GX::TexMtx texMtx, GX::PTTexMtx pttTexMtx); static void HandleTransparency(u32& finalTevCount, u32& finalKColorCount, const CModelFlags& modelFlags, u32 blendFactors, u32& finalCCFlags, u32& finalACFlags); static u32 HandleReflection(bool usesTevReg2, u32 indTexSlot, u32 r5, u32 finalTevCount, u32 texCount, u32 tcgCount, diff --git a/Runtime/Graphics/CCubeModel.cpp b/Runtime/Graphics/CCubeModel.cpp index 9bb93f4a0..29ba2a578 100644 --- a/Runtime/Graphics/CCubeModel.cpp +++ b/Runtime/Graphics/CCubeModel.cpp @@ -176,7 +176,7 @@ void CCubeModel::DrawFlat(TConstVectorRef positions, TConstVectorRef normals, ES void CCubeModel::DrawNormal(TConstVectorRef positions, TConstVectorRef normals, ESurfaceSelection surfaces) { CGX::SetNumIndStages(0); CGX::SetNumTevStages(1); - CGX::SetNumTexGens(1); + CGX::SetNumTexGens(1); // TODO should this be 0? CGX::SetZMode(true, GX::LEQUAL, true); CGX::SetTevOrder(GX::TEVSTAGE0, GX::TEXCOORD_NULL, GX::TEXMAP_NULL, GX::COLOR_NULL); CGX::SetTevColorIn(GX::TEVSTAGE0, GX::CC_ZERO, GX::CC_ZERO, GX::CC_ZERO, GX::CC_ZERO); diff --git a/Runtime/Graphics/CGX.hpp b/Runtime/Graphics/CGX.hpp index a09d656f9..134952125 100644 --- a/Runtime/Graphics/CGX.hpp +++ b/Runtime/Graphics/CGX.hpp @@ -44,7 +44,7 @@ struct SGXState { u16 x56_blendMode = 0; std::array x58_kColors; std::array x68_tevStates; - std::array x228_texStates; + std::array x228_texStates; u32 x248_alphaCompare = 0; float x24c_fogStartZ = 0.f; float x250_fogEndZ = 0.f; @@ -367,9 +367,25 @@ static inline void SetTevOrder(GX::TevStageID stageId, GX::TexCoordID texCoord, } } -static inline void SetTexCoordGen(GX::TexCoordID dstCoord, GX::TexGenType fn, GX::TexGenSrc src, u32 mtx, - GXBool normalize, u32 postMtx) noexcept { - // TODO +static inline void SetTexCoordGen(GX::TexCoordID dstCoord, GX::TexGenType fn, GX::TexGenSrc src, GX::TexMtx mtx, + GXBool normalize, GX::PTTexMtx postMtx) noexcept { + u32 flags = ((postMtx - GX::PTTEXMTX0) & 63) << 15 | (u8(normalize) & 1) << 14 | ((mtx - GX::TEXMTX0) & 31) << 9 | + (src & 31) << 4 | (fn & 15); + auto& state = sGXState.x228_texStates[dstCoord].x0_coordGen; + if (flags != state) { + state = flags; + GXSetTexCoordGen2(dstCoord, fn, src, mtx, normalize, postMtx); + } +} + +static inline void SetTexCoordGen(GX::TexCoordID dstCoord, u32 flags) noexcept { + auto& state = sGXState.x228_texStates[dstCoord].x0_coordGen; + if (flags != state) { + state = flags; + GXSetTexCoordGen2(dstCoord, GX::TexGenType(flags & 15), GX::TexGenSrc(flags >> 4 & 31), + GX::TexMtx((flags >> 9 & 31) + GX::TEXMTX0), GXBool(flags >> 14 & 1), + GX::PTTexMtx((flags >> 15 & 63) + GX::PTTEXMTX0)); + } } void SetVtxDescv(GX::VtxDescList* descList) noexcept; diff --git a/Runtime/Graphics/CGraphics.cpp b/Runtime/Graphics/CGraphics.cpp index 30058dec4..699d272a1 100644 --- a/Runtime/Graphics/CGraphics.cpp +++ b/Runtime/Graphics/CGraphics.cpp @@ -185,8 +185,7 @@ void CGraphics::Render2D(CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus:: const auto oldLights = g_LightActive; SetOrtho(-g_Viewport.x10_halfWidth, g_Viewport.x10_halfWidth, g_Viewport.x14_halfHeight, -g_Viewport.x14_halfHeight, -1.f, -10.f); - // disable Y/Z swap TODO do we need to do this elsewhere? - aurora::gfx::update_model_view(zeus::CMatrix4f{}, zeus::CMatrix4f{}); + GXLoadPosMtxImm({}, GX::PNMTX0); DisableAllLights(); SetCullMode(ERglCullMode::None); tex.Load(GX::TEXMAP0, EClampMode::Repeat); @@ -252,12 +251,13 @@ void CGraphics::SetViewMatrix() { else g_GXModelView = g_CameraMatrix * g_GXModelMatrix; /* Load position matrix */ + GXLoadPosMtxImm(g_GXModelView, GX::PNMTX0); /* Inverse-transpose */ g_GXModelViewInvXpose = g_GXModelView.inverse(); g_GXModelViewInvXpose.origin.zeroOut(); g_GXModelViewInvXpose.basis.transpose(); /* Load normal matrix */ - aurora::gfx::update_model_view(g_GXModelView.toMatrix4f(), g_GXModelViewInvXpose.toMatrix4f()); + GXLoadNrmMtxImm(g_GXModelViewInvXpose, GX::PNMTX0); } void CGraphics::SetModelMatrix(const zeus::CTransform& xf) { @@ -365,12 +365,14 @@ void CGraphics::SetOrtho(float left, float right, float top, float bottom, float } void CGraphics::FlushProjection() { + const auto mtx = GetPerspectiveProjectionMatrix(); if (g_Proj.x0_persp) { // Convert and load persp + GXSetProjection(mtx, GX::PERSPECTIVE); } else { // Convert and load ortho + GXSetProjection(mtx, GX::ORTHOGRAPHIC); } - aurora::gfx::update_projection(GetPerspectiveProjectionMatrix()); } zeus::CVector2i CGraphics::ProjectPoint(const zeus::CVector3f& point) { @@ -460,17 +462,19 @@ void CGraphics::SetViewport(int leftOff, int bottomOff, int width, int height) { CachedVP.position[1] = bottomOff; CachedVP.size[0] = width; CachedVP.size[1] = height; - aurora::gfx::set_viewport(CachedVP, g_CachedDepthRange[0], g_CachedDepthRange[1]); + GXSetViewport(CachedVP.position[0], CachedVP.position[1], CachedVP.size[0], CachedVP.size[1], g_CachedDepthRange[0], + g_CachedDepthRange[1]); } void CGraphics::SetScissor(int leftOff, int bottomOff, int width, int height) { - aurora::gfx::set_scissor(leftOff, bottomOff, width, height); + GXSetScissor(leftOff, bottomOff, width, height); } void CGraphics::SetDepthRange(float znear, float zfar) { g_CachedDepthRange[0] = znear; g_CachedDepthRange[1] = zfar; - aurora::gfx::set_viewport(CachedVP, g_CachedDepthRange[0], g_CachedDepthRange[1]); + GXSetViewport(CachedVP.position[0], CachedVP.position[1], CachedVP.size[0], CachedVP.size[1], g_CachedDepthRange[0], + g_CachedDepthRange[1]); } CTimeProvider* CGraphics::g_ExternalTimeProvider = nullptr; @@ -600,7 +604,8 @@ void CGraphics::SetTevStates(EStreamFlags flags) noexcept { } CGX::SetNumChans(1); CGX::SetNumIndStages(0); - // TODO load TCGs + 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); const bool hasLights = g_LightActive.any(); CGX::SetChanCtrl(CGX::EChannelId::Channel0, hasLights, GX::SRC_REG, flags & EStreamFlagBits::fHasColor ? GX::SRC_VTX : GX::SRC_REG, g_LightActive, diff --git a/Runtime/Graphics/GX.hpp b/Runtime/Graphics/GX.hpp index 4872cb6f2..4c39cb9b2 100644 --- a/Runtime/Graphics/GX.hpp +++ b/Runtime/Graphics/GX.hpp @@ -6,6 +6,8 @@ #include #include +#include +#include namespace GX { enum Attr { @@ -246,7 +248,8 @@ enum TexGenSrc { TG_TEXCOORD5, TG_TEXCOORD6, TG_COLOR0, - TG_COLOR1 + TG_COLOR1, + MAX_TEXGENSRC = 0xFF, }; enum TexMtx { @@ -644,6 +647,24 @@ enum FogType { FOG_ORTHO_REVEXP2 = 0x0F, }; +enum PosNrmMtx { + PNMTX0, + PNMTX1, + PNMTX2, + PNMTX3, + PNMTX4, + PNMTX5, + PNMTX6, + PNMTX7, + PNMTX8, + PNMTX9, +}; + +enum ProjectionType { + PERSPECTIVE, + ORTHOGRAPHIC, +}; + } // namespace GX using GXColor = zeus::CColor; @@ -684,4 +705,13 @@ void GXClearVtxDesc() noexcept; void GXSetArray(GX::Attr attr, const void* data, u8 stride) noexcept; void GXSetTevDirect(GX::TevStageID stageId) noexcept; void GXSetFog(GX::FogType type, float startZ, float endZ, float nearZ, float farZ, const GXColor& color) noexcept; +void GXSetFogColor(const GXColor& color) noexcept; void GXCallDisplayList(const void* data, u32 nbytes) noexcept; +void GXSetTexCoordGen2(GX::TexCoordID dst, GX::TexGenType type, GX::TexGenSrc src, GX::TexMtx mtx, GXBool normalize, + GX::PTTexMtx postMtx) noexcept; +void GXLoadTexMtxImm(const void* data, u32 id /* GX::TexMtx or GX::PTTexMtx */, GX::TexMtxType type) noexcept; +void GXLoadPosMtxImm(const zeus::CTransform& xf, GX::PosNrmMtx id) noexcept; +void GXLoadNrmMtxImm(const zeus::CTransform& xf, GX::PosNrmMtx id) noexcept; +void GXSetProjection(const zeus::CMatrix4f& mtx, GX::ProjectionType type) noexcept; +void GXSetViewport(float left, float top, float width, float height, float nearZ, float farZ) noexcept; +void GXSetScissor(u32 left, u32 top, u32 width, u32 height) noexcept; diff --git a/aurora/include/aurora/common.hpp b/aurora/include/aurora/common.hpp index 7a0305b48..fb4a34002 100644 --- a/aurora/include/aurora/common.hpp +++ b/aurora/include/aurora/common.hpp @@ -15,6 +15,7 @@ struct Vec2 { T x{}; T y{}; + constexpr Vec2() = default; constexpr Vec2(T x, T y) : x(x), y(y) {} constexpr Vec2(const zeus::CVector2f& vec) : x(vec.x()), y(vec.y()) {} }; @@ -24,6 +25,7 @@ struct Vec3 { T y{}; T z{}; + constexpr Vec3() = default; constexpr Vec3(T x, T y, T z) : x(x), y(y), z(z) {} constexpr Vec3(const zeus::CVector3f& vec) : x(vec.x()), y(vec.y()), z(vec.z()) {} }; @@ -34,20 +36,34 @@ struct Vec4 { T z{}; T w{}; + constexpr Vec4() = default; constexpr Vec4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {} constexpr Vec4(const zeus::CVector4f& vec) : x(vec.x()), y(vec.y()), z(vec.z()), w(vec.w()) {} constexpr Vec4(const zeus::CColor& color) : x(color.r()), y(color.g()), z(color.b()), w(color.a()) {} }; template +struct Mat4x2 { + Vec2 m0{}; + Vec2 m1{}; + Vec2 m2{}; + Vec2 m3{}; + + constexpr Mat4x2() = default; + constexpr Mat4x2(const Vec2& m0, const Vec2& m1, const Vec2& m2, const Vec2& m3) + : m0(m0), m1(m1), m2(m2), m3(m3) {} +}; +template struct Mat4x4 { Vec4 m0{}; Vec4 m1{}; Vec4 m2{}; Vec4 m3{}; + constexpr Mat4x4() = default; constexpr Mat4x4(const Vec4& m0, const Vec4& m1, const Vec4& m2, const Vec4& m3) : m0(m0), m1(m1), m2(m2), m3(m3) {} constexpr Mat4x4(const zeus::CMatrix4f& m) : m0(m[0]), m1(m[1]), m2(m[2]), m3(m[3]) {} + constexpr Mat4x4(const zeus::CTransform& m) : Mat4x4(m.toMatrix4f()) {} }; constexpr Mat4x4 Mat4x4_Identity{ Vec4{1.f, 0.f, 0.f, 0.f}, diff --git a/aurora/include/aurora/gfx.hpp b/aurora/include/aurora/gfx.hpp index 124ee2d13..165456223 100644 --- a/aurora/include/aurora/gfx.hpp +++ b/aurora/include/aurora/gfx.hpp @@ -141,12 +141,10 @@ void stream_end() noexcept; void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept; void unbind_texture(GX::TexMapID id) noexcept; -void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept; -void update_projection(const zeus::CMatrix4f& proj) noexcept; void update_fog_state(const metaforce::CFogState& state) noexcept; void load_light(GX::LightID id, const Light& light) noexcept; void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept; -void set_viewport(const zeus::CRectangle& rect, float znear, float zfar) noexcept; +void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept; void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept; void resolve_color(const ClipRect& rect, uint32_t bind, bool clear_depth) noexcept; diff --git a/aurora/lib/gfx/common.cpp b/aurora/lib/gfx/common.cpp index 0aa4a7d13..de7422654 100644 --- a/aurora/lib/gfx/common.cpp +++ b/aurora/lib/gfx/common.cpp @@ -55,7 +55,10 @@ struct Command { CommandType type; union Data { struct SetViewportCommand { - zeus::CRectangle rect; + float left; + float top; + float width; + float height; float znear; float zfar; } setViewport; @@ -443,8 +446,7 @@ void render(const wgpu::RenderPassEncoder& pass) { switch (cmd.type) { case CommandType::SetViewport: { const auto& vp = cmd.data.setViewport; - pass.SetViewport(vp.rect.position.x(), vp.rect.position.y(), vp.rect.size.x(), vp.rect.size.y(), vp.znear, - vp.zfar); + pass.SetViewport(vp.left, vp.top, vp.width, vp.height, vp.znear, vp.zfar); } break; case CommandType::SetScissor: { const auto& sc = cmd.data.setScissor; diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index 59427b09a..eaba1fd17 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -116,7 +116,73 @@ void GXSetAlphaCompare(GX::Compare comp0, float ref0, GX::AlphaOp op, GX::Compar unreachable(); } } -void GXSetVtxDescv(GX::VtxDescList* list) noexcept; +void GXSetTexCoordGen2(GX::TexCoordID dst, GX::TexGenType type, GX::TexGenSrc src, GX::TexMtx mtx, GXBool normalize, + GX::PTTexMtx postMtx) noexcept { + if (dst < GX::TEXCOORD0 || dst > GX::TEXCOORD7) { + Log.report(logvisor::Fatal, FMT_STRING("invalid tex coord {}"), dst); + unreachable(); + } + g_gxState.tcgs[dst] = {type, src, mtx, postMtx, normalize}; +} +void GXLoadTexMtxImm(const void* data, u32 id, GX::TexMtxType type) noexcept { + if ((id < GX::TEXMTX0 || id > GX::IDENTITY) && (id < GX::PTTEXMTX0 || id > GX::PTIDENTITY)) { + Log.report(logvisor::Fatal, FMT_STRING("invalid tex mtx {}"), id); + unreachable(); + } + if (id >= GX::PTTEXMTX0) { + if (type != GX::MTX3x4) { + Log.report(logvisor::Fatal, FMT_STRING("invalid pt mtx type {}"), type); + unreachable(); + } + const auto idx = (id - GX::PTTEXMTX0) / 3; + g_gxState.ptTexMtxs[idx] = *static_cast(data); + } else { + const auto idx = (id - GX::TEXMTX0) / 3; + switch (type) { + case GX::MTX3x4: + g_gxState.texMtxs[idx] = aurora::Mat4x4{*static_cast(data)}; + break; + case GX::MTX2x4: + g_gxState.texMtxs[idx] = *static_cast*>(data); + break; + } + } +} +void GXLoadPosMtxImm(const zeus::CTransform& xf, GX::PosNrmMtx id) noexcept { + if (id != GX::PNMTX0) { + Log.report(logvisor::Fatal, FMT_STRING("invalid pn mtx {}"), id); + unreachable(); + } + g_gxState.mv = xf.toMatrix4f(); +} +void GXLoadNrmMtxImm(const zeus::CTransform& xf, GX::PosNrmMtx id) noexcept { + if (id != GX::PNMTX0) { + Log.report(logvisor::Fatal, FMT_STRING("invalid pn mtx {}"), id); + unreachable(); + } + g_gxState.mvInv = xf.toMatrix4f(); +} +constexpr zeus::CMatrix4f DepthCorrect{ + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f, + // clang-format on +}; +void GXSetProjection(const zeus::CMatrix4f& mtx, GX::ProjectionType type) noexcept { + if (type == GX::PERSPECTIVE) { + g_gxState.proj = DepthCorrect * mtx; + } else { + g_gxState.proj = mtx; + } +} +void GXSetViewport(float left, float top, float width, float height, float nearZ, float farZ) noexcept { + aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ); +} +void GXSetScissor(u32 left, u32 top, u32 width, u32 height) noexcept { + aurora::gfx::set_scissor(left, top, width, height); +} namespace aurora::gfx { static logvisor::Module Log("aurora::gfx::gx"); @@ -130,19 +196,6 @@ void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHan } void unbind_texture(GX::TexMapID id) noexcept { gx::g_gxState.textures[static_cast(id)].reset(); } -void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept { - gx::g_gxState.mv = mv; - gx::g_gxState.mvInv = mv_inv; -} -constexpr zeus::CMatrix4f DepthCorrect{ - // clang-format off - 1.f, 0.f, 0.f, 0.f, - 0.f, 1.f, 0.f, 0.f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f, - // clang-format on -}; -void update_projection(const zeus::CMatrix4f& proj) noexcept { gx::g_gxState.proj = DepthCorrect * proj; } void update_fog_state(const metaforce::CFogState& state) noexcept { gx::g_gxState.fogState = state; } void load_light(GX::LightID id, const Light& light) noexcept { gx::g_gxState.lights[std::log2(id)] = light; } @@ -338,6 +391,9 @@ ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primit for (u8 i = 0; i < g_gxState.numChans; ++i) { config.shaderConfig.colorChannels[i] = g_gxState.colorChannelConfig[i]; } + for (u8 i = 0; i < g_gxState.numTexGens; ++i) { + config.shaderConfig.tcgs[i] = g_gxState.tcgs[i]; + } config.shaderConfig.alphaDiscard = g_gxState.alphaDiscard; config = { .shaderConfig = config.shaderConfig, @@ -412,6 +468,39 @@ Range build_uniform(const ShaderInfo& info) noexcept { } buf.append(&g_gxState.kcolors[i], 16); } + for (int i = 0; i < info.usesTexMtx.size(); ++i) { + if (!info.usesTexMtx.test(i)) { + continue; + } + switch (info.texMtxTypes[i]) { + case GX::TG_MTX2x4: + if (std::holds_alternative>(g_gxState.texMtxs[i])) { + buf.append(&std::get>(g_gxState.texMtxs[i]), 32); + } else { + Log.report(logvisor::Fatal, FMT_STRING("expected 2x4 mtx in idx {}"), i); + unreachable(); + } + break; + case GX::TG_MTX3x4: + if (std::holds_alternative>(g_gxState.texMtxs[i])) { + const auto& mat = std::get>(g_gxState.texMtxs[i]); + buf.append(&mat, 64); + } else { + // Log.report(logvisor::Fatal, FMT_STRING("expected 3x4 mtx in idx {}"), i); + buf.append(&Mat4x4_Identity, 64); + } + break; + default: + Log.report(logvisor::Fatal, FMT_STRING("unhandled tex mtx type {}"), info.texMtxTypes[i]); + unreachable(); + } + } + for (int i = 0; i < info.usesPTTexMtx.size(); ++i) { + if (!info.usesPTTexMtx.test(i)) { + continue; + } + buf.append(&g_gxState.ptTexMtxs[i], 48); + } for (int i = 0; i < info.sampledTextures.size(); ++i) { if (!info.sampledTextures.test(i)) { continue; diff --git a/aurora/lib/gfx/gx.hpp b/aurora/lib/gfx/gx.hpp index b40e87cce..e10ef594a 100644 --- a/aurora/lib/gfx/gx.hpp +++ b/aurora/lib/gfx/gx.hpp @@ -10,6 +10,9 @@ constexpr u32 MaxTevStages = GX::MAX_TEVSTAGE; constexpr u32 MaxColorChannels = 2; // COLOR0A0, COLOR1A1 constexpr u32 MaxTevRegs = 3; // TEVREG0-2 constexpr u32 MaxKColors = GX::MAX_KCOLOR; +constexpr u32 MaxTexMtx = 10; +constexpr u32 MaxPTTexMtx = 20; +constexpr u32 MaxTexCoord = GX::MAX_TEXCOORD; template struct TevPass { @@ -61,6 +64,15 @@ struct ColorChannelState { GX::LightMask lightState; }; using LightVariant = std::variant; +// Mat4x4 used instead of Mat4x3 for padding purposes +using TexMtxVariant = std::variant, Mat4x4>; +struct TcgConfig { + GX::TexGenType type = GX::TG_MTX2x4; + GX::TexGenSrc src = GX::MAX_TEXGENSRC; + GX::TexMtx mtx = GX::IDENTITY; + GX::PTTexMtx postMtx = GX::PTIDENTITY; + bool normalize = false; +}; struct GXState { zeus::CMatrix4f mv; @@ -83,6 +95,9 @@ struct GXState { std::array lights; std::array tevStages; std::array textures; + std::array texMtxs; + std::array, MaxPTTexMtx> ptTexMtxs; + std::array tcgs; bool depthCompare = true; bool depthUpdate = true; bool alphaUpdate = true; @@ -101,6 +116,7 @@ const TextureBind& get_texture(GX::TexMapID id) noexcept; struct ShaderConfig { std::array, MaxTevStages> tevStages; std::array colorChannels; + std::array tcgs; std::optional alphaDiscard; bool denormalizedVertexAttributes = false; bool denormalizedHasNrm = false; // TODO this is a hack @@ -133,6 +149,9 @@ struct ShaderInfo { std::bitset sampledKColors; std::bitset sampledColorChannels; std::bitset usesTevReg; + std::bitset usesTexMtx; + std::bitset usesPTTexMtx; + std::array texMtxTypes; u32 uniformSize = 0; bool usesVtxColor : 1 = false; bool usesNormal : 1 = false; @@ -204,6 +223,14 @@ inline void xxh3_update(XXH3_state_t& state, const gfx::gx::ColorChannelConfig& } } template <> +inline void xxh3_update(XXH3_state_t& state, const gfx::gx::TcgConfig& input) { + XXH3_64bits_update(&state, &input.type, sizeof(gfx::gx::TcgConfig::type)); + XXH3_64bits_update(&state, &input.src, sizeof(gfx::gx::TcgConfig::src)); + XXH3_64bits_update(&state, &input.mtx, sizeof(gfx::gx::TcgConfig::mtx)); + XXH3_64bits_update(&state, &input.postMtx, sizeof(gfx::gx::TcgConfig::postMtx)); + XXH3_64bits_update(&state, &input.normalize, sizeof(gfx::gx::TcgConfig::normalize)); +} +template <> inline void xxh3_update(XXH3_state_t& state, const gfx::gx::ShaderConfig& input) { for (const auto& item : input.tevStages) { if (!item) { @@ -214,6 +241,9 @@ inline void xxh3_update(XXH3_state_t& state, const gfx::gx::ShaderConfig& input) for (const auto& item : input.colorChannels) { xxh3_update(state, item); } + for (const auto& item : input.tcgs) { + xxh3_update(state, item); + } if (input.alphaDiscard) { XXH3_64bits_update(&state, &*input.alphaDiscard, sizeof(float)); } diff --git a/aurora/lib/gfx/gx_shader.cpp b/aurora/lib/gfx/gx_shader.cpp index 294ab94b6..76d4d7ed5 100644 --- a/aurora/lib/gfx/gx_shader.cpp +++ b/aurora/lib/gfx/gx_shader.cpp @@ -334,6 +334,16 @@ static std::string_view tev_scale(GX::TevScale scale) { } } +static std::string in_uv(u32 idx) { + if (idx == 0) { + return "v_packed_uvs.data[in_uv_0_4_idx[0]]"; + } + if (idx < 4) { + return fmt::format(FMT_STRING("v_uvs.data[in_uv_0_4_idx[{}]]"), idx); + } + return fmt::format(FMT_STRING("v_uvs.data[in_uv_5_7_idx[{}]]"), idx - 4); +} + std::pair build_shader(const ShaderConfig& config) noexcept { const auto hash = xxh3_hash(config); if (g_gxCachedShaders.contains(hash)) { @@ -376,10 +386,19 @@ std::pair build_shader(const ShaderConfig& confi Log.report(logvisor::Info, FMT_STRING(" texMapId: {}"), stage->texMapId); Log.report(logvisor::Info, FMT_STRING(" channelId: {}"), stage->channelId); } - // for (int i = 0; i < config.channelMatSrcs.size(); ++i) { - // Log.report(logvisor::Info, FMT_STRING(" channelMatSrcs[{}]: {}"), i, config.channelMatSrcs[i]); - // } - // Log.report(logvisor::Info, FMT_STRING(" alphaDiscard: {}"), config.alphaDiscard); + for (int i = 0; i < config.colorChannels.size(); ++i) { + const auto& chan = config.colorChannels[i]; + Log.report(logvisor::Info, FMT_STRING(" colorChannels[{}]: enabled {} mat {} amb {}"), i, chan.lightingEnabled, + chan.matSrc, chan.ambSrc); + } + for (int i = 0; i < config.tcgs.size(); ++i) { + const auto& tcg = config.tcgs[i]; + if (tcg.src != GX::MAX_TEXGENSRC) { + Log.report(logvisor::Info, FMT_STRING(" tcg[{}]: src {} mtx {} post {} type {} norm {}"), i, tcg.src, tcg.mtx, + tcg.postMtx, tcg.type, tcg.normalize); + } + } + Log.report(logvisor::Info, FMT_STRING(" alphaDiscard: {}"), config.alphaDiscard.value_or(0.f)); Log.report(logvisor::Info, FMT_STRING(" denormalizedVertexAttributes: {}"), config.denormalizedVertexAttributes); } @@ -593,6 +612,78 @@ var v_packed_uvs: Vec2Block; info.uniformSize += 16; } size_t texBindIdx = 0; + for (int i = 0; i < info.sampledTextures.size(); ++i) { + if (!info.sampledTextures.test(i)) { + continue; + } + const auto& tcg = config.tcgs[i]; + if (config.denormalizedVertexAttributes) { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); + vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2"), locIdx + 1, i); + // TODO check tcg src for denorm? + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0} = vec4(in_tex{0}_uv, 0.0, 1.0);"), i); + } else { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); + if (tcg.src >= GX::TG_TEX0 && tcg.src <= GX::TG_TEX7) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4({}, 0.0, 1.0);"), i, in_uv(tcg.src - GX::TG_TEX0)); + } else if (tcg.src == GX::TG_POS) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4(obj_pos.xyz, 1.0);"), i); + } else if (tcg.src == GX::TG_NRM) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{} = vec4(obj_norm.xyz, 1.0);"), i); + } else { + Log.report(logvisor::Fatal, FMT_STRING("unhandled tcg src {}"), tcg.src); + unreachable(); + } + } + // TODO this all assumes MTX3x4 currently + if (tcg.mtx == GX::IDENTITY) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_tmp = tc{0}.xyz;"), i); + } else { + u32 texMtxIdx = (tcg.mtx - GX::TEXMTX0) / 3; + info.usesTexMtx.set(texMtxIdx); + info.texMtxTypes[texMtxIdx] = tcg.type; + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_tmp = ubuf.texmtx{1} * tc{0};"), i, texMtxIdx); + } + if (tcg.normalize) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n tc{0}_tmp = normalize(tc{0}_tmp);"), i); + } + if (tcg.postMtx == GX::PTIDENTITY) { + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_proj = tc{0}_tmp;"), i); + } else { + u32 postMtxIdx = (tcg.postMtx - GX::PTTEXMTX0) / 3; + info.usesPTTexMtx.set(postMtxIdx); + vtxXfrAttrs += fmt::format(FMT_STRING("\n var tc{0}_proj = ubuf.postmtx{1} * vec4(tc{0}_tmp.xyz, 1.0);"), i, postMtxIdx); + } + vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = tc{0}_proj.xy;"), i); + fragmentFnPre += fmt::format( + FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"), i); + locIdx++; + } + for (int i = 0; i < info.usesTexMtx.size(); ++i) { + if (!info.usesTexMtx.test(i)) { + continue; + } + switch (info.texMtxTypes[i]) { + case GX::TG_MTX2x4: + uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x2;"), i); + info.uniformSize += 32; + break; + case GX::TG_MTX3x4: + uniBufAttrs += fmt::format(FMT_STRING("\n texmtx{}: mat4x3;"), i); + info.uniformSize += 64; + break; + default: + Log.report(logvisor::Fatal, FMT_STRING("unhandled tex mtx type {}"), info.texMtxTypes[i]); + unreachable(); + } + } + for (int i = 0; i < info.usesPTTexMtx.size(); ++i) { + if (!info.usesPTTexMtx.test(i)) { + continue; + } + uniBufAttrs += fmt::format(FMT_STRING("\n postmtx{}: mat4x3;"), i); + info.uniformSize += 64; + } for (int i = 0; i < info.sampledTextures.size(); ++i) { if (!info.sampledTextures.test(i)) { continue; @@ -607,29 +698,9 @@ var v_packed_uvs: Vec2Block; "var tex{}: texture_2d;"), texBindIdx, i); ++texBindIdx; - - if (config.denormalizedVertexAttributes) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2"), locIdx + 1, i); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = in_tex{0}_uv;"), i); - } else { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); - if (i < 4) { - if (i == 0) { - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{}_uv = v_packed_uvs.data[in_uv_0_4_idx[{}]];"), i, i); - } else { - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{}_uv = v_uvs.data[in_uv_0_4_idx[{}]];"), i, i); - } - } else { - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{}_uv = v_uvs.data[in_uv_5_7_idx[{}]];"), i, i - 4); - } - } - fragmentFnPre += fmt::format( - FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"), i); - locIdx++; } if (config.alphaDiscard) { - fragmentFn += fmt::format(FMT_STRING("\n if (prev.a < {}f) {{ discard; }}"), *config.alphaDiscard); + fragmentFn += fmt::format(FMT_STRING("\n if (prev.a < {}f) {{ discard; }}"), *config.alphaDiscard); } const auto shaderSource =