#pragma once #include "Runtime/ConsoleVariables/CVar.hpp" #include "Runtime/Graphics/CLight.hpp" #include "Runtime/Graphics/CTevCombiners.hpp" #include "Runtime/Graphics/GX.hpp" #include "Runtime/RetroTypes.hpp" #include #include #include #include #include #include #include #include #include using frame_clock = std::chrono::high_resolution_clock; namespace metaforce { class CTexture; extern CVar* g_disableLighting; class CTimeProvider; enum class ERglCullMode : std::underlying_type_t { None = GX::CULL_NONE, Front = GX::CULL_FRONT, Back = GX::CULL_BACK, All = GX::CULL_ALL, }; enum class ERglBlendMode : std::underlying_type_t { None = GX::BM_NONE, Blend = GX::BM_BLEND, Logic = GX::BM_LOGIC, Subtract = GX::BM_SUBTRACT, Max = GX::MAX_BLENDMODE, }; enum class ERglBlendFactor : std::underlying_type_t { Zero = GX::BL_ZERO, One = GX::BL_ONE, SrcColor = GX::BL_SRCCLR, InvSrcColor = GX::BL_INVSRCCLR, SrcAlpha = GX::BL_SRCALPHA, InvSrcAlpha = GX::BL_INVSRCALPHA, DstAlpha = GX::BL_DSTALPHA, InvDstAlpha = GX::BL_INVDSTALPHA, DstColor = GX::BL_DSTCLR, InvDstColor = GX::BL_INVDSTCLR, }; enum class ERglLogicOp : std::underlying_type_t { Clear = GX::LO_CLEAR, And = GX::LO_AND, RevAnd = GX::LO_REVAND, Copy = GX::LO_COPY, InvAnd = GX::LO_INVAND, NoOp = GX::LO_NOOP, Xor = GX::LO_XOR, Or = GX::LO_OR, Nor = GX::LO_NOR, Equiv = GX::LO_EQUIV, Inv = GX::LO_INV, RevOr = GX::LO_REVOR, InvCopy = GX::LO_INVCOPY, InvOr = GX::LO_INVOR, NAnd = GX::LO_NAND, Set = GX::LO_SET, }; enum class ERglAlphaFunc : std::underlying_type_t { Never = GX::NEVER, Less = GX::LESS, Equal = GX::EQUAL, LEqual = GX::LEQUAL, Greater = GX::GREATER, NEqual = GX::NEQUAL, GEqual = GX::GEQUAL, Always = GX::ALWAYS, }; enum class ERglAlphaOp : std::underlying_type_t { And = GX::AOP_AND, Or = GX::AOP_OR, Xor = GX::AOP_XOR, XNor = GX::AOP_XNOR, Max = GX::MAX_ALPHAOP, }; enum class ERglEnum : std::underlying_type_t { Never = GX::NEVER, Less = GX::LESS, Equal = GX::EQUAL, LEqual = GX::LEQUAL, Greater = GX::GREATER, NEqual = GX::NEQUAL, GEqual = GX::GEQUAL, Always = GX::ALWAYS, }; using ERglLight = u8; enum class ERglTexOffset : std::underlying_type_t { Zero = GX::TO_ZERO, Sixteenth = GX::TO_SIXTEENTH, Eighth = GX::TO_EIGHTH, Fourth = GX::TO_FOURTH, Half = GX::TO_HALF, One = GX::TO_ONE, }; enum class ERglFogMode : std::underlying_type_t { None = GX::FOG_NONE, PerspLin = GX::FOG_PERSP_LIN, PerspExp = GX::FOG_PERSP_EXP, PerspExp2 = GX::FOG_ORTHO_EXP2, PerspRevExp = GX::FOG_PERSP_REVEXP, PerspRevExp2 = GX::FOG_PERSP_REVEXP2, OrthoLin = GX::FOG_ORTHO_LIN, OrthoExp = GX::FOG_ORTHO_EXP, OrthoExp2 = GX::FOG_ORTHO_EXP2, OrthoRevExp = GX::FOG_ORTHO_REVEXP, OrthoRevExp2 = GX::FOG_ORTHO_REVEXP2, }; struct SViewport { u32 x0_left; u32 x4_top; u32 x8_width; u32 xc_height; float x10_halfWidth; float x14_halfHeight; float aspect; }; struct SClipScreenRect { bool x0_valid = false; int x4_left = 0; int x8_top = 0; int xc_width = 0; int x10_height = 0; int x14_dstWidth = 0; float x18_uvXMin = 0.f; float x1c_uvXMax = 0.f; float x20_uvYMin = 0.f; float x24_uvYMax = 0.f; SClipScreenRect() = default; SClipScreenRect(bool valid, int left, int top, int width, int height, int dstWidth, float uvXMin, float uvXMax, float uvYMin, float uvYMax) : x0_valid(valid) , x4_left(left) , x8_top(top) , xc_width(width) , x10_height(height) , x14_dstWidth(dstWidth) , x18_uvXMin(uvXMin) , x1c_uvXMax(uvXMax) , x20_uvYMin(uvYMin) , x24_uvYMax(uvYMax) {} SClipScreenRect(const aurora::gfx::ClipRect& rect) { x4_left = rect.x; x8_top = rect.y; xc_width = rect.width; x10_height = rect.height; x14_dstWidth = rect.width; } SClipScreenRect(const SViewport& vp) { x4_left = vp.x0_left; x8_top = vp.x4_top; xc_width = vp.x8_width; x10_height = vp.xc_height; } }; #define DEPTH_FAR 1.f #define DEPTH_SKY 0.999f #define DEPTH_TARGET_MANAGER 0.12500012f #define DEPTH_WORLD (1.f / 8.f) #define DEPTH_GUN (1.f / 32.f) #define DEPTH_SCREEN_ACTORS (1.f / 64.f) #define DEPTH_HUD (1.f / 512.f) #define DEPTH_NEAR 0.f #define CUBEMAP_RES 256 #define CUBEMAP_MIPS 6 class CGraphics { public: struct CProjectionState { bool x0_persp; float x4_left; float x8_right; float xc_top; float x10_bottom; float x14_near; float x18_far; }; static CProjectionState g_Proj; static zeus::CVector2f g_CachedDepthRange; // static CFogState g_Fog; static SViewport g_Viewport; static float g_ProjAspect; static u32 g_NumBreakpointsWaiting; static u32 g_FlippingState; static bool g_LastFrameUsedAbove; static bool g_InterruptLastFrameUsedAbove; static GX::LightMask g_LightActive; static std::array g_LightObjs; static std::array g_LightTypes; static zeus::CTransform g_GXModelView; static zeus::CTransform g_GXModelViewInvXpose; static zeus::CTransform g_GXModelMatrix; static zeus::CTransform g_ViewMatrix; static zeus::CVector3f g_ViewPoint; static zeus::CTransform g_GXViewPointMatrix; static zeus::CTransform g_CameraMatrix; static SClipScreenRect g_CroppedViewport; static bool g_IsGXModelMatrixIdentity; static zeus::CColor g_ClearColor; static float g_ClearDepthValue; // Was a 24bit value, we use a float range from [0,1] static bool g_IsBeginSceneClearFb; static ERglEnum g_depthFunc; static ERglCullMode g_cullMode; static void Startup(); static void InitGraphicsVariables(); static void InitGraphicsDefaults(); static void SetDefaultVtxAttrFmt(); static void DisableAllLights(); static void LoadLight(ERglLight light, const CLight& info); static void EnableLight(ERglLight light); static void SetLightState(GX::LightMask lightState); static void SetAmbientColor(const zeus::CColor& col); static void SetFog(ERglFogMode mode, float startz, float endz, const zeus::CColor& color); static void SetDepthWriteMode(bool test, ERglEnum comp, bool write); static void SetBlendMode(ERglBlendMode, ERglBlendFactor, ERglBlendFactor, ERglLogicOp); static void SetCullMode(ERglCullMode); static void BeginScene(); static void EndScene(); static void Render2D(CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus::CColor& col); static bool BeginRender2D(const CTexture& tex); static void DoRender2D(const CTexture& tex, s32 x, s32 y, s32 w1, s32 w2, s32 w3, s32 w4, s32 w5, const zeus::CColor& col); static void EndRender2D(bool v); static void SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1); static void SetViewPointMatrix(const zeus::CTransform& xf); static void SetViewMatrix(); static void SetModelMatrix(const zeus::CTransform& xf); static zeus::CMatrix4f CalculatePerspectiveMatrix(float fovy, float aspect, float znear, float zfar); static zeus::CMatrix4f GetPerspectiveProjectionMatrix(); static const CProjectionState& GetProjectionState(); static void SetProjectionState(const CProjectionState&); static void SetPerspective(float fovy, float aspect, float znear, float zfar); static void SetOrtho(float left, float right, float top, float bottom, float znear, float zfar); static void FlushProjection(); static zeus::CVector2i ProjectPoint(const zeus::CVector3f& point); static SClipScreenRect ClipScreenRectFromMS(const zeus::CVector3f& p1, const zeus::CVector3f& p2); static SClipScreenRect ClipScreenRectFromVS(const zeus::CVector3f& p1, const zeus::CVector3f& p2); static void SetViewportResolution(const zeus::CVector2i& res); static void SetViewport(int leftOff, int bottomOff, int width, int height); static void SetScissor(int leftOff, int bottomOff, int width, int height); static void SetDepthRange(float near, float far); static CTimeProvider* g_ExternalTimeProvider; static float g_DefaultSeconds; static u32 g_RenderTimings; static void SetExternalTimeProvider(CTimeProvider* provider) { g_ExternalTimeProvider = provider; } static float GetSecondsMod900(); static void TickRenderTimings(); static u32 g_FrameCounter; static u32 g_Framerate; static u32 g_FramesPast; static frame_clock::time_point g_FrameStartTime; static u32 GetFrameCounter() { return g_FrameCounter; } static u32 GetFPS() { return g_Framerate; } static void UpdateFPSCounter(); static void SetUseVideoFilter(bool); static void SetClearColor(const zeus::CColor& color); static void SetCopyClear(const zeus::CColor& color, float depth); static void SetIsBeginSceneClearFb(bool clear); static u32 GetViewportLeft() { return g_Viewport.x0_left; } static u32 GetViewportTop() { return g_Viewport.x4_top; } static u32 GetViewportWidth() { return g_Viewport.x8_width; } static u32 GetViewportHeight() { return g_Viewport.xc_height; } static float GetViewportHalfWidth() { return g_Viewport.x10_halfWidth; } static float GetViewportHalfHeight() { return g_Viewport.x14_halfHeight; } static float GetViewportAspect() { return g_Viewport.aspect; } static bool IsCroppedViewportValid() { return g_CroppedViewport.x0_valid; } static int GetCroppedViewportLeft() { return g_CroppedViewport.x4_left; } static int GetCroppedViewportTop() { return g_CroppedViewport.x8_top; } static int GetCroppedViewportWidth() { return g_CroppedViewport.xc_width; } static int GetCroppedViewportHeight() { return g_CroppedViewport.x10_height; } static float GetCroppedViewportDstWidth() { return g_CroppedViewport.x14_dstWidth; } static float GetCroppedViewportUVXMin() { return g_CroppedViewport.x18_uvXMin; } static float GetCroppedViewportUVXMax() { return g_CroppedViewport.x1c_uvXMax; } static float GetCroppedViewportUVYMin() { return g_CroppedViewport.x20_uvYMin; } static float GetCroppedViewportUVYMax() { return g_CroppedViewport.x24_uvYMax; } static const std::array skCubeBasisMats; static void ResolveSpareTexture(const SClipScreenRect& rect, int bindIdx, GX::TextureFormat format, bool clearDepth = false) { aurora::gfx::resolve_color({rect.x4_left, rect.x8_top, rect.xc_width, rect.x10_height}, bindIdx, format, clearDepth); } static void LoadDolphinSpareTexture(int bindIdx, GX::TextureFormat format, GX::TexMapID id) { GXTexObj obj; GXInitTexObjResolved(&obj, bindIdx, format, GX_CLAMP, GX_CLAMP); GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR, 0.f, 0.f, 0.f, false, false, GX_ANISO_1); GXLoadTexObj(&obj, id); } static void ResolveSpareDepth(const SClipScreenRect& rect, int bindIdx = 0) { // aurora::gfx::resolve_depth({rect.x4_left, rect.x8_top, rect.xc_width, rect.x10_height}, bindIdx); } static void ResetGfxStates() noexcept; static void SetTevStates(EStreamFlags flags) noexcept; static void SetTevOp(ERglTevStage stage, const CTevCombiners::CTevPass& pass); static void StreamBegin(GX::Primitive primitive); static void StreamNormal(const zeus::CVector3f& nrm); static void StreamColor(const zeus::CColor& color); static inline void StreamColor(float r, float g, float b, float a) { StreamColor({r, g, b, a}); } static void StreamTexcoord(const zeus::CVector2f& uv); static inline void StreamTexcoord(float x, float y) { StreamTexcoord({x, y}); } static void StreamVertex(const zeus::CVector3f& pos); static inline void StreamVertex(float xyz) { StreamVertex({xyz, xyz, xyz}); } static inline void StreamVertex(float x, float y, float z) { StreamVertex({x, y, z}); } static void StreamEnd(); static void UpdateVertexDataStream(); static void ResetVertexDataStream(bool end); static void FlushStream(); static void FullRender(); static void DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* pos, const zeus::CVector3f& normal, const zeus::CColor& col, s32 numVerts); static void SetLineWidth(float width, ERglTexOffset offs); }; template class TriFanToStrip { std::vector& m_vec; size_t m_start; size_t m_added = 0; public: explicit TriFanToStrip(std::vector& vec) : m_vec(vec), m_start(vec.size()) {} void AddVert(const VTX& vert) { ++m_added; if (m_added > 3 && (m_added & 1) == 0) { m_vec.reserve(m_vec.size() + 3); m_vec.push_back(m_vec.back()); m_vec.push_back(m_vec[m_start]); } m_vec.push_back(vert); } template void EmplaceVert(_Args&&... args) { ++m_added; if (m_added > 3 && (m_added & 1) == 0) { m_vec.reserve(m_vec.size() + 3); m_vec.push_back(m_vec.back()); m_vec.push_back(m_vec[m_start]); } m_vec.emplace_back(std::forward<_Args>(args)...); } // void Draw() const { CGraphics::DrawArray(m_start, m_vec.size() - m_start); } }; #ifdef AURORA_GFX_DEBUG_GROUPS #define SCOPED_GRAPHICS_DEBUG_GROUP(name, ...) \ OPTICK_EVENT_DYNAMIC(name); \ aurora::gfx::ScopedDebugGroup _GfxDbg_{name} #else #define SCOPED_GRAPHICS_DEBUG_GROUP(name, ...) OPTICK_EVENT_DYNAMIC(name) #endif } // namespace metaforce