From 17136fdfb909ef76f76e0d8177cfba2a2e400349 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Tue, 10 May 2022 01:20:09 -0700 Subject: [PATCH] Implement GX lighting API (need to update shader generation) --- Runtime/Graphics/CGraphics.cpp | 56 ++++++++- Runtime/Graphics/CGraphics.hpp | 4 +- Runtime/Graphics/CLight.hpp | 1 + Runtime/Graphics/GX.hpp | 78 ++++++++++++ aurora/lib/gfx/gx.cpp | 223 +++++++++++++++++++++++++++++++++ 5 files changed, 357 insertions(+), 5 deletions(-) diff --git a/Runtime/Graphics/CGraphics.cpp b/Runtime/Graphics/CGraphics.cpp index a56c889ef..234bc151c 100644 --- a/Runtime/Graphics/CGraphics.cpp +++ b/Runtime/Graphics/CGraphics.cpp @@ -1,7 +1,6 @@ #include "Runtime/Graphics/CGraphics.hpp" #include "Runtime/CTimeProvider.hpp" -#include "Runtime/Graphics/CLight.hpp" #include "Runtime/Graphics/CLineRenderer.hpp" #include "Runtime/Graphics/CTexture.hpp" #include "Runtime/Graphics/Shaders/CTextSupportShader.hpp" @@ -19,6 +18,8 @@ u32 CGraphics::g_FlippingState; bool CGraphics::g_LastFrameUsedAbove = false; bool CGraphics::g_InterruptLastFrameUsedAbove = false; GX::LightMask CGraphics::g_LightActive{}; +std::array CGraphics::g_LightObjs; +std::array CGraphics::g_LightTypes; zeus::CTransform CGraphics::g_GXModelView; zeus::CTransform CGraphics::g_GXModelViewInvXpose; zeus::CTransform CGraphics::g_GXModelMatrix = zeus::CTransform(); @@ -80,6 +81,45 @@ void CGraphics::DisableAllLights() { void CGraphics::LoadLight(ERglLight light, const CLight& info) { const auto lightId = static_cast(1 << light); + +#if 0 + zeus::CVector3f pos = info.GetPosition(); + zeus::CVector3f dir = info.GetDirection(); + if (info.GetType() == ELightType::Directional) { + return; + dir = -(g_CameraMatrix.buildMatrix3f() * dir); + GXInitLightPos(&g_LightObjs[static_cast(light)], dir.x() * 1048576.f, dir.y() * 1048576.f, dir.z() * 1048576.f); + GXInitLightAttn(&g_LightObjs[static_cast(light)], 1.f, 0.f, 0.f, 1.f, 0.f, 0.f); + } else if (info.GetType() == ELightType::Spot) { + pos = g_CameraMatrix * pos; + GX::LightObj* obj = &g_LightObjs[static_cast(light)]; + GXInitLightPos(obj, pos.x(), pos.y(), pos.z()); + dir = g_CameraMatrix.buildMatrix3f() * dir; + GXInitLightDir(obj, dir.x(), dir.y(), dir.z()); + GXInitLightAttn(obj, 1.f, 0.f, 0.f, info.GetAttenuationConstant(), info.GetAngleAttenuationLinear(), + info.GetAttenuationQuadratic()); + GXInitLightSpot(obj, info.GetSpotCutoff(), GX::SP_COS2); + } else if (info.GetType() == ELightType::Custom) { + pos = g_CameraMatrix * pos; + GX::LightObj* obj = &g_LightObjs[static_cast(light)]; + GXInitLightPos(obj, pos.x(), pos.y(), pos.z()); + dir = g_CameraMatrix.buildMatrix3f() * dir; + GXInitLightDir(obj, dir.x(), dir.y(), dir.z()); + GXInitLightAttn(obj, info.GetAngleAttenuationConstant(), info.GetAngleAttenuationLinear(), + info.GetAngleAttenuationQuadratic(), info.GetAttenuationConstant(), + info.GetAngleAttenuationLinear(), info.GetAttenuationQuadratic()); + } else if (info.GetType() == ELightType::LocalAmbient) { + pos = g_CameraMatrix * pos; + GXInitLightPos(&g_LightObjs[static_cast(light)], pos.x(), pos.y(), pos.z()); + GXInitLightAttn(&g_LightObjs[static_cast(light)], 1.f, 0.f, 0.f, info.GetAttenuationConstant(), + info.GetAngleAttenuationLinear(), info.GetAttenuationQuadratic()); + } + + g_LightTypes[static_cast(light)] = info.GetType(); + GX::Color col(info.GetColor().r(), info.GetColor().g(), info.GetColor().b()); + GXInitLightColor(&g_LightObjs[static_cast(light)], col); + GXLoadLightObjImm(&g_LightObjs[static_cast(light)], lightId); +#else switch (info.GetType()) { case ELightType::LocalAmbient: aurora::gfx::load_light_ambient(lightId, info.GetColor()); @@ -103,6 +143,7 @@ void CGraphics::LoadLight(ERglLight light, const CLight& info) { break; } } +#endif } void CGraphics::EnableLight(ERglLight light) { @@ -674,7 +715,14 @@ void CGraphics::Startup() { } void CGraphics::InitGraphicsVariables() { - // g_lightTypes[0..n] = Directional; + g_LightTypes[0] = ELightType::Directional; + g_LightTypes[1] = ELightType::Directional; + g_LightTypes[2] = ELightType::Directional; + g_LightTypes[3] = ELightType::Directional; + g_LightTypes[4] = ELightType::Directional; + g_LightTypes[5] = ELightType::Directional; + g_LightTypes[6] = ELightType::Directional; + g_LightTypes[7] = ELightType::Directional; g_LightActive = {}; SetDepthWriteMode(false, g_depthFunc, false); SetCullMode(ERglCullMode::None); @@ -682,8 +730,8 @@ void CGraphics::InitGraphicsVariables() { g_IsGXModelMatrixIdentity = false; // SetIdentityViewPointMatrix(); // SetIdentityModelMatrix(); - // SetViewport(...); - // SetPerspective(...); + SetViewport(0, 0, g_Viewport.x8_width, g_Viewport.xc_height); + SetPerspective(60.f, g_Viewport.x8_width / g_Viewport.xc_height, g_Proj.x14_near, g_Proj.x18_far); SetCopyClear(g_ClearColor, 1.f); CGX::SetChanMatColor(CGX::EChannelId::Channel0, zeus::skWhite); // g_RenderState.ResetFlushAll(); diff --git a/Runtime/Graphics/CGraphics.hpp b/Runtime/Graphics/CGraphics.hpp index 1c004568c..a23fb06af 100644 --- a/Runtime/Graphics/CGraphics.hpp +++ b/Runtime/Graphics/CGraphics.hpp @@ -1,6 +1,7 @@ #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" @@ -22,7 +23,6 @@ using frame_clock = std::chrono::high_resolution_clock; namespace metaforce { class CTexture; extern CVar* g_disableLighting; -class CLight; class CTimeProvider; enum class ERglCullMode : std::underlying_type_t { @@ -214,6 +214,8 @@ public: 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; diff --git a/Runtime/Graphics/CLight.hpp b/Runtime/Graphics/CLight.hpp index ca5556881..fbf60e1f4 100644 --- a/Runtime/Graphics/CLight.hpp +++ b/Runtime/Graphics/CLight.hpp @@ -72,6 +72,7 @@ public: x4c_24_intensityDirty = true; x4c_25_radiusDirty = true; } + float GetSpotCutoff() const { return x20_spotCutoff; } float GetAttenuationConstant() const { return x24_distC; } float GetAttenuationLinear() const { return x28_distL; } float GetAttenuationQuadratic() const { return x2c_distQ; } diff --git a/Runtime/Graphics/GX.hpp b/Runtime/Graphics/GX.hpp index aadc0f653..99089ad93 100644 --- a/Runtime/Graphics/GX.hpp +++ b/Runtime/Graphics/GX.hpp @@ -1,5 +1,9 @@ #pragma once +#ifndef GX_IS_WII +#define GX_IS_WII 0 +#endif + #include "../GCNTypes.hpp" #include @@ -10,6 +14,12 @@ #include namespace GX { +#if GX_IS_WII +constexpr float LARGE_NUMBER = -1.0e+18f; +#else +constexpr float LARGE_NUMBER = -1048576.0f; +#endif + enum Attr { VA_PNMTXIDX = 0x0, VA_TEX0MTXIDX = 0x1, @@ -692,6 +702,39 @@ enum CompType { RGBA8 = 5, }; +struct LightObj { + u32 __padding[3]; + GX::Color color; + float a0 = 1.f; + float a1 = 0.f; + float a2 = 0.f; + float k0 = 1.f; + float k1 = 0.f; + float k2 = 0.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float nx = 0.f; + float ny = 0.f; + float nz = 0.f; +}; + +enum SpotFn { + SP_OFF, + SP_FLAT, + SP_COS, + SP_COS2, + SP_SHARP, + SP_RING1, + SP_RING2, +}; + +enum DistAttnFn { + DA_OFF, + DA_GENTLE, + DA_MEDIUM, + DA_STEEP, +}; } // namespace GX using GXColor = zeus::CColor; @@ -759,3 +802,38 @@ void GXEnd() noexcept; void GXSetTevSwapModeTable(GX::TevSwapSel id, GX::TevColorChan red, GX::TevColorChan green, GX::TevColorChan blue, GX::TevColorChan alpha) noexcept; void GXSetTevSwapMode(GX::TevStageID stage, GX::TevSwapSel rasSel, GX::TevSwapSel texSel) noexcept; + +// Lighting +void GXInitLightAttn(GX::LightObj* light, float a0, float a1, float a2, float k0, float k1, float k2); +void GXInitLightAttnA(GX::LightObj* light, float a0, float a1, float a2); +void GXInitLightAttnK(GX::LightObj* light, float k0, float k1, float k2); +void GXInitLightSpot(GX::LightObj* light, float cutoff, GX::SpotFn spotFn); +void GXInitLightDistAttn(GX::LightObj* light, float refDistance, float refBrightness, GX::DistAttnFn distFunc); +constexpr void GXInitLightShininess(GX::LightObj* light, float shininess) { + GXInitLightAttn(light, 0.f, 0.f, 1.f, (shininess) / 2.f, 0.f, 1.f - (shininess) / 2.f); +} +void GXInitLightPos(GX::LightObj* light, float x, float y, float z); +constexpr void GXInitLightPosv(GX::LightObj* light, float vec[3]) { GXInitLightPos(light, vec[0], vec[1], vec[2]); } +void GXInitLightDir(GX::LightObj* light, float nx, float ny, float nz); +constexpr void GXInitLightDirv(GX::LightObj* light, float vec[3]) { GXInitLightDir(light, vec[0], vec[1], vec[2]); } +void GXInitSpecularDir(GX::LightObj* light, float nx, float ny, float nz); +void GXInitSpecularDirHA(GX::LightObj* light, float nx, float ny, float nz, float hx, float hy, float hz); +constexpr void GXInitLightSpecularDirHAv(GX::LightObj* light, float vecn[3], float vech[3]) { + GXInitSpecularDirHA(light, vecn[0], vecn[1], vecn[2], vech[0], vech[1], vech[2]); +} +void GXInitLightColor(GX::LightObj* light, GX::Color col); +void GXLoadLightObjImm(const GX::LightObj* light, GX::LightID id); +void GXLoadLightObjIndx(u32 index, GX::LightID); + +void GXGetLightAttnA(const GX::LightObj* light, float* a0, float* a1, float* a2); +void GXGetLightAttnK(const GX::LightObj* light, float* k0, float* k1, float* k2); +void GXGetLightPos(const GX::LightObj* light, float* x, float* y, float* z); +constexpr void GXGetLightPosv(const GX::LightObj* light, float* vec[3]) { + GXGetLightPos(light, vec[0], vec[1], vec[2]); +} +void GXGetLightDir(const GX::LightObj* light, float* nx, float* ny, float* nz); +constexpr void GXGetLightDirv(const GX::LightObj* light, float* vec[3]) { + GXGetLightDir(light, vec[0], vec[1], vec[2]); +} + +void GXGetLightColor(const GX::LightObj* light, GX::Color* col); \ No newline at end of file diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index fb7e8aaec..577882f76 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -207,6 +207,229 @@ void GXSetLineWidth(u8 width, GX::TexOffset offs) noexcept { // TODO } +// Lighting +void GXInitLightAttn(GX::LightObj* light, float a0, float a1, float a2, float k0, float k1, float k2) { + light->a0 = a0; + light->a1 = a1; + light->a2 = a2; + light->k0 = k0; + light->k1 = k1; + light->k2 = k2; +} + +void GXInitLightAttnA(GX::LightObj* light, float a0, float a1, float a2) { + light->a0 = a0; + light->a1 = a1; + light->a2 = a2; +} + +void GXInitLightAttnK(GX::LightObj* light, float k0, float k1, float k2) { + light->k0 = k0; + light->k1 = k1; + light->k2 = k2; +} + +void GXInitLightSpot(GX::LightObj* light, float cutoff, GX::SpotFn spotFn) { + if (cutoff < 0.f || cutoff > 90.f) { + spotFn = GX::SP_OFF; + } + + float cr = (cutoff * M_PIF) / 180.f; + float a0 = 1.f; + float a1 = 0.f; + float a2 = 0.f; + switch (spotFn) { + case GX::SP_OFF: + a0 = 1.f; + a1 = 0.f; + a2 = 0.f; + break; + case GX::SP_FLAT: + a0 = -1000.f * cr; + a1 = 1000.f; + a2 = 0.f; + break; + case GX::SP_COS: + a0 = -cr / (1.f - cr); + a1 = 1.f / (1.f - cr); + a2 = 0.f; + break; + case GX::SP_COS2: + a0 = 0.0f; + a1 = -cr / (1.f - cr); + a2 = 1.f / (1.f - cr); + break; + case GX::SP_SHARP: { + const float d = (1.f - cr) * (1.f - cr); + a0 = cr * (cr - 2.f); + a1 = 2.f / d; + a2 = -1.f / d; + break; + } + case GX::SP_RING1: { + const float d = (1.f - cr) * (1.f - cr); + a0 = 4.f * cr / d; + a1 = 4.f * (1.f + cr) / d; + a2 = -4.f / d; + break; + } + case GX::SP_RING2: { + const float d = (1.f - cr) * (1.f - cr); + a0 = 1.f - 2.f * cr * cr / d; + a1 = 4.f * cr / d; + a2 = -2.f / d; + break; + } + } + + light->a0 = a0; + light->a1 = a1; + light->a2 = a2; +} + +void GXInitLightDistAttn(GX::LightObj* light, float refDistance, float refBrightness, GX::DistAttnFn distFunc) { + if (refDistance < 0.f || refBrightness < 0.f || refBrightness >= 1.f) { + distFunc = GX::DA_OFF; + } + float k0 = 1.f; + float k1 = 0.f; + float k2 = 0.f; + switch (distFunc) { + case GX::DA_GENTLE: + k0 = 1.0f; + k1 = (1.0f - refBrightness) / (refBrightness * refDistance); + k2 = 0.0f; + break; + case GX::DA_MEDIUM: + k0 = 1.0f; + k1 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance); + k2 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance * refDistance); + break; + case GX::DA_STEEP: + k0 = 1.0f; + k1 = 0.0f; + k2 = (1.0f - refBrightness) / (refBrightness * refDistance * refDistance); + break; + case GX::DA_OFF: + k0 = 1.0f; + k1 = 0.0f; + k2 = 0.0f; + break; + } + + light->k0 = k0; + light->k1 = k1; + light->k2 = k2; +} + +void GXInitLightPos(GX::LightObj* light, float x, float y, float z) { + light->px = x; + light->py = y; + light->pz = z; +} + +void GXInitLightDir(GX::LightObj* light, float nx, float ny, float nz) { + light->nx = nx; + light->ny = ny; + light->nz = nz; +} +void GXInitSpecularDir(GX::LightObj* light, float nx, float ny, float nz) { + float hx = -nx; + float hy = -ny; + float hz = (-nz + 1.0f); + float mag = ((hx * hx) + (hy * hy) + (hz * hz)); + if (mag != 0.0f) { + mag = 1.0f / sqrtf(mag); + } + light->px = (nx * GX::LARGE_NUMBER); + light->py = (ny * GX::LARGE_NUMBER); + light->pz = (nz * GX::LARGE_NUMBER); + light->nx = hx * mag; + light->ny = hy * mag; + light->nz = hz * mag; +} + +void GXInitSpecularDirHA(GX::LightObj* light, float nx, float ny, float nz, float hx, float hy, float hz) { + light->px = (nx * GX::LARGE_NUMBER); + light->py = (ny * GX::LARGE_NUMBER); + light->pz = (nz * GX::LARGE_NUMBER); + light->nx = hx; + light->ny = hy; + light->nz = hz; +} + +void GXInitLightColor(GX::LightObj* light, GX::Color col) { light->color = col; } + +void GXLoadLightObjImm(const GX::LightObj* light, GX::LightID id) { + u32 idx = 0; + switch (id) { + case GX::LIGHT0: + idx = 0; + break; + case GX::LIGHT1: + idx = 1; + break; + case GX::LIGHT2: + idx = 2; + break; + case GX::LIGHT3: + idx = 3; + break; + case GX::LIGHT4: + idx = 4; + break; + case GX::LIGHT5: + idx = 5; + break; + case GX::LIGHT6: + idx = 6; + break; + case GX::LIGHT7: + idx = 7; + break; + default: + idx = 0; + break; + } + + aurora::gfx::Light realLight; + realLight.pos.assign(light->px, light->py, light->pz); + realLight.dir.assign(light->nx, light->ny, light->nz); + realLight.angAtt.assign(light->a0, light->a1, light->a2); + realLight.linAtt.assign(light->k0, light->k1, light->k2); + realLight.color.fromRGBA32(light->color.num); + g_gxState.lights[idx] = realLight; +} + +/* TODO Figure out a way to implement this, requires GXSetArray */ +void GXLoadLightObjIndx(u32 index, GX::LightID) {} + +void GXGetLightAttnA(const GX::LightObj* light, float* a0, float* a1, float* a2) { + *a0 = light->a0; + *a1 = light->a1; + *a2 = light->a2; +} + +void GXGetLightAttnK(const GX::LightObj* light, float* k0, float* k1, float* k2) { + *k0 = light->k0; + *k1 = light->k1; + *k2 = light->k2; +} + +void GXGetLightPos(const GX::LightObj* light, float* x, float* y, float* z) { + *x = light->px; + *z = light->py; + *z = light->pz; +} + +void GXGetLightDir(const GX::LightObj* light, float* nx, float* ny, float* nz) { + *nx = light->nx; + *ny = light->ny; + *nz = light->nz; +} + +void GXGetLightColor(const GX::LightObj* light, GX::Color* col) { *col = light->color; } + namespace aurora::gfx { static logvisor::Module Log("aurora::gfx::gx");