From 763d4b8b0a630ac82978c84db4d4ae54b7cdc9a4 Mon Sep 17 00:00:00 2001 From: parax0 Date: Sun, 25 Oct 2015 23:36:53 -0600 Subject: [PATCH] Added support for dynamic lighting in Metroid Prime 3 + some other dynamic lighting additions and fixes --- Core/CGraphics.cpp | 4 ++ Core/CLightParameters.cpp | 25 ++++++++++ Core/CLightParameters.h | 18 +++++++ PrimeWorldEditor.pro | 6 ++- Resource/CLight.cpp | 10 ++++ Resource/CLight.h | 4 ++ Resource/factory/CAreaLoader.cpp | 84 +++++++++++++++++++++++++++++++- Resource/factory/CAreaLoader.h | 1 + Scene/CLightNode.cpp | 6 ++- Scene/CSceneNode.cpp | 72 +++++++++++++++++++-------- Scene/CSceneNode.h | 4 ++ Scene/CScriptNode.cpp | 10 ++-- Scene/CScriptNode.h | 3 ++ Scene/CStaticNode.cpp | 2 +- 14 files changed, 218 insertions(+), 31 deletions(-) create mode 100644 Core/CLightParameters.cpp create mode 100644 Core/CLightParameters.h diff --git a/Core/CGraphics.cpp b/Core/CGraphics.cpp index 96f6c3ee..8141b162 100644 --- a/Core/CGraphics.cpp +++ b/Core/CGraphics.cpp @@ -156,6 +156,10 @@ void CGraphics::SetDefaultLighting() sDefaultDirectionalLights[0].Load(); sDefaultDirectionalLights[1].Load(); sDefaultDirectionalLights[2].Load(); + UpdateLightBlock(); + + sVertexBlock.COLOR0_Amb = CColor::skGray.ToVector4f(); + UpdateVertexBlock(); } void CGraphics::SetIdentityMVP() diff --git a/Core/CLightParameters.cpp b/Core/CLightParameters.cpp new file mode 100644 index 00000000..a623b18b --- /dev/null +++ b/Core/CLightParameters.cpp @@ -0,0 +1,25 @@ +#include "CLightParameters.h" + +CLightParameters::CLightParameters(CPropertyStruct *pStruct, EGame game) + : mpStruct(pStruct), mGame(game) +{ +} + +CLightParameters::~CLightParameters() +{ +} + +int CLightParameters::LightLayerIndex() +{ + if (!mpStruct) return 0; + + CLongProperty *pParam; + + if (mGame <= ePrime) + pParam = (CLongProperty*) mpStruct->PropertyByIndex(0xD); + else + pParam = (CLongProperty*) mpStruct->PropertyByID(0x1F715FD3); + + if (!pParam) return 0; + else return pParam->Get(); +} diff --git a/Core/CLightParameters.h b/Core/CLightParameters.h new file mode 100644 index 00000000..a85860cf --- /dev/null +++ b/Core/CLightParameters.h @@ -0,0 +1,18 @@ +#ifndef CLIGHTPARAMETERS_H +#define CLIGHTPARAMETERS_H + +#include +#include + +class CLightParameters +{ + CPropertyStruct *mpStruct; + EGame mGame; + +public: + CLightParameters(CPropertyStruct *pStruct, EGame game); + ~CLightParameters(); + int LightLayerIndex(); +}; + +#endif // CLIGHTPARAMETERS_H diff --git a/PrimeWorldEditor.pro b/PrimeWorldEditor.pro index 58da7d79..009d78cc 100644 --- a/PrimeWorldEditor.pro +++ b/PrimeWorldEditor.pro @@ -152,7 +152,8 @@ SOURCES += \ Resource/CAnimationParameters.cpp \ UI/WAnimParamsEditor.cpp \ Resource/CCollisionMeshGroup.cpp \ - Core/CFrustumPlanes.cpp + Core/CFrustumPlanes.cpp \ + Core/CLightParameters.cpp HEADERS += \ Common/AnimUtil.h \ @@ -320,7 +321,8 @@ HEADERS += \ Resource/CAnimationParameters.h \ UI/WAnimParamsEditor.h \ Resource/CCollisionMeshGroup.h \ - Core/CFrustumPlanes.h + Core/CFrustumPlanes.h \ + Core/CLightParameters.h FORMS += \ UI/CWorldEditorWindow.ui \ diff --git a/Resource/CLight.cpp b/Resource/CLight.cpp index 7fbaa1c7..88d3f2b9 100644 --- a/Resource/CLight.cpp +++ b/Resource/CLight.cpp @@ -88,6 +88,11 @@ ELightType CLight::GetType() const return mType; } +u32 CLight::GetLayerIndex() const +{ + return mLayerIndex; +} + CVector3f CLight::GetPosition() const { return mPosition; @@ -136,6 +141,11 @@ float CLight::GetIntensity() } // ************ SETTERS ************ +void CLight::SetLayer(u32 index) +{ + mLayerIndex = index; +} + void CLight::SetPosition(const CVector3f& Position) { mPosition = Position; diff --git a/Resource/CLight.h b/Resource/CLight.h index 6e882ae3..72d439c8 100644 --- a/Resource/CLight.h +++ b/Resource/CLight.h @@ -23,12 +23,14 @@ enum ELightType class CLight { ELightType mType; + u32 mLayerIndex; CVector3f mPosition; CVector3f mDirection; CColor mColor; float mSpotCutoff; CVector3f mDistAttenCoefficients; CVector3f mAngleAttenCoefficients; + float mRadius; float mIntensity; u8 mFlags; @@ -45,6 +47,7 @@ private: public: // Getters ELightType GetType() const; + u32 GetLayerIndex() const; CVector3f GetPosition() const; CVector3f GetDirection() const; CColor GetColor() const; @@ -54,6 +57,7 @@ public: float GetIntensity(); // Setters + void SetLayer(u32 index); void SetPosition(const CVector3f& Position); void SetDirection(const CVector3f& Direction); void SetColor(const CColor& Color); diff --git a/Resource/factory/CAreaLoader.cpp b/Resource/factory/CAreaLoader.cpp index 2cd3286b..3dae1ef5 100644 --- a/Resource/factory/CAreaLoader.cpp +++ b/Resource/factory/CAreaLoader.cpp @@ -198,6 +198,7 @@ void CAreaLoader::ReadLightsPrime() 1.f, 0.f, 0.f); } + Light->SetLayer(ly); mpArea->mLightLayers[ly][l] = Light; } } @@ -336,7 +337,87 @@ void CAreaLoader::ReadGeometryCorruption() std::cout << "\n"; } -// ************ RETURNS ************ +void CAreaLoader::ReadLightsCorruption() +{ + Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)"); + mBlockMgr->ToBlock(mLightsBlockNum); + + u32 babedead = mpMREA->ReadLong(); + if (babedead != 0xbabedead) return; + + mpArea->mLightLayers.resize(4); + + for (u32 iLayer = 0; iLayer < 4; iLayer++) + { + u32 NumLights = mpMREA->ReadLong(); + mpArea->mLightLayers[iLayer].resize(NumLights); + + for (u32 iLight = 0; iLight < NumLights; iLight++) + { + ELightType Type = (ELightType) mpMREA->ReadLong(); + + float r = mpMREA->ReadFloat(); + float g = mpMREA->ReadFloat(); + float b = mpMREA->ReadFloat(); + float a = mpMREA->ReadFloat(); + CColor LightColor(r, g, b, a); + + CVector3f Position(*mpMREA); + CVector3f Direction(*mpMREA); + mpMREA->Seek(0xC, SEEK_CUR); + + float Multiplier = mpMREA->ReadFloat(); + float SpotCutoff = mpMREA->ReadFloat(); + mpMREA->Seek(0x9, SEEK_CUR); + u32 FalloffType = mpMREA->ReadLong(); + mpMREA->Seek(0x18, SEEK_CUR); + + // Relevant data is read - now we process and form a CLight out of it + CLight *Light; + + if (Multiplier < FLT_EPSILON) + Multiplier = FLT_EPSILON; + + // Local Ambient + if (Type == eLocalAmbient) + { + Light = CLight::BuildLocalAmbient(Position, LightColor * Multiplier); + } + + // Directional + else if (Type == eDirectional) + { + Light = CLight::BuildDirectional(Position, Direction, LightColor); + } + + // Spot + else if (Type == eSpot) + { + Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff); + + float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f; + float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f; + float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f; + Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC); + } + + // Custom + else + { + float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f; + float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f; + float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f; + + Light = CLight::BuildCustom(Position, Direction, LightColor, + DistAttenA, DistAttenB, DistAttenC, + 1.f, 0.f, 0.f); + } + + Light->SetLayer(iLayer); + mpArea->mLightLayers[iLayer][iLight] = Light; + } + } +} // ************ COMMON ************ void CAreaLoader::ReadCompressedBlocks() @@ -508,6 +589,7 @@ CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA) Loader.ReadGeometryCorruption(); Loader.ReadSCLYEchoes(); Loader.ReadCollision(); + if (Loader.mVersion == eCorruption) Loader.ReadLightsCorruption(); break; default: Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + StringUtil::ToHexString(version)); diff --git a/Resource/factory/CAreaLoader.h b/Resource/factory/CAreaLoader.h index be684c13..64760472 100644 --- a/Resource/factory/CAreaLoader.h +++ b/Resource/factory/CAreaLoader.h @@ -67,6 +67,7 @@ class CAreaLoader // Corruption void ReadHeaderCorruption(); void ReadGeometryCorruption(); + void ReadLightsCorruption(); // Common void ReadCompressedBlocks(); diff --git a/Scene/CLightNode.cpp b/Scene/CLightNode.cpp index 80b03cee..b8e3b6df 100644 --- a/Scene/CLightNode.cpp +++ b/Scene/CLightNode.cpp @@ -45,7 +45,11 @@ void CLightNode::Draw(ERenderOptions) CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(1.f); CGraphics::sVertexBlock.COLOR0_Mat = CVector4f(1.f); CGraphics::UpdateVertexBlock(); - CDrawUtil::DrawShadedCube(mpLight->GetColor()); + + // Force alpha to 0 to prevent issues with bloom + CColor cubeColor = mpLight->GetColor(); + cubeColor.a = 0; + CDrawUtil::DrawShadedCube(cubeColor); // Below commented-out code is for light radius visualization as a bounding box /*float r = mLight->GetRadius(); diff --git a/Scene/CSceneNode.cpp b/Scene/CSceneNode.cpp index a17bbe70..57853f52 100644 --- a/Scene/CSceneNode.cpp +++ b/Scene/CSceneNode.cpp @@ -26,6 +26,9 @@ CSceneNode::CSceneNode(CSceneManager *pScene, CSceneNode *pParent) _mInheritsRotation = true; _mInheritsScale = true; + mLightLayerIndex = 0; + mLightCount = 0; + mMouseHovering = false; mSelected = false; mVisible = true; @@ -115,8 +118,10 @@ void CSceneNode::LoadModelMatrix() void CSceneNode::BuildLightList(CGameArea *pArea) { mLightCount = 0; + mAmbientColor = CColor::skBlack; - u32 LayerCount = pArea->GetLightLayerCount(); + u32 index = mLightLayerIndex; + if ((pArea->GetLightLayerCount() <= index) || (pArea->GetLightCount(index) == 0)) index = 0; struct SLightEntry { CLight *pLight; @@ -131,28 +136,27 @@ void CSceneNode::BuildLightList(CGameArea *pArea) }; std::vector LightEntries; - for (u32 iLayer = 0; iLayer < LayerCount; iLayer++) + // Default ambient color to white if there are no lights on the selected layer + u32 numLights = pArea->GetLightCount(index); + if (numLights == 0) mAmbientColor = CColor::skWhite; + + for (u32 iLight = 0; iLight < numLights; iLight++) { - u32 LightCount = pArea->GetLightCount(iLayer); + CLight* pLight = pArea->GetLight(index, iLight); - for (u32 iLight = 0; iLight < LightCount; iLight++) + // Ambient lights should only be present one per layer; need to check how the game deals with multiple ambients + if (pLight->GetType() == eLocalAmbient) + mAmbientColor = pLight->GetColor(); + + // Other lights will be used depending which are closest to the node + else { - CLight* pLight = pArea->GetLight(iLayer, iLight); + bool IsInRange = AABox().IntersectsSphere(pLight->GetPosition(), pLight->GetRadius()); - // Directional/Spot lights take priority over custom lights. - if ((pLight->GetType() == eDirectional) || (pLight->GetType() == eSpot)) - LightEntries.push_back(SLightEntry(pLight, 0)); - - // Custom lights will be used depending which are closest to the node - else if (pLight->GetType() == eCustom) + if (IsInRange) { - bool IsInRange = AABox().IntersectsSphere(pLight->GetPosition(), pLight->GetRadius()); - - if (IsInRange) - { - float Dist = mPosition.Distance(pLight->GetPosition()); - LightEntries.push_back(SLightEntry(pLight, Dist)); - } + float Dist = mPosition.Distance(pLight->GetPosition()); + LightEntries.push_back(SLightEntry(pLight, Dist)); } } } @@ -169,13 +173,29 @@ void CSceneNode::LoadLights() { CGraphics::sNumLights = 0; - if (CGraphics::sLightMode == CGraphics::BasicLighting) - CGraphics::SetDefaultLighting(); + switch (CGraphics::sLightMode) + { + case CGraphics::NoLighting: + // No lighting: default ambient color, no dynamic lights + CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); + break; + + case CGraphics::BasicLighting: + // Basic lighting: default ambient color, default dynamic lights + CGraphics::SetDefaultLighting(); + CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); + break; + + case CGraphics::WorldLighting: + // World lighting: world ambient color, node dynamic lights + CGraphics::sVertexBlock.COLOR0_Amb = mAmbientColor.ToVector4f(); - else if (CGraphics::sLightMode == CGraphics::WorldLighting) for (u32 iLight = 0; iLight < mLightCount; iLight++) mLights[iLight]->Load(); + break; + } + CGraphics::UpdateLightBlock(); } @@ -340,6 +360,11 @@ CVector3f CSceneNode::CenterPoint() return AABox().Center(); } +u32 CSceneNode::LightLayerIndex() const +{ + return mLightLayerIndex; +} + bool CSceneNode::MarkedVisible() const { // The reason I have this function is because the instance view needs to know whether a node is marked @@ -403,6 +428,11 @@ void CSceneNode::SetScale(const CVector3f& scale) MarkTransformChanged(); } +void CSceneNode::SetLightLayerIndex(u32 index) +{ + mLightLayerIndex = index; +} + void CSceneNode::SetMouseHovering(bool Hovering) { mMouseHovering = Hovering; diff --git a/Scene/CSceneNode.h b/Scene/CSceneNode.h index aab3277e..357319d2 100644 --- a/Scene/CSceneNode.h +++ b/Scene/CSceneNode.h @@ -44,8 +44,10 @@ protected: bool mVisible; std::list mChildren; + u32 mLightLayerIndex; u32 mLightCount; CLight* mLights[8]; + CColor mAmbientColor; public: explicit CSceneNode(CSceneManager *pScene, CSceneNode *pParent = 0); @@ -89,6 +91,7 @@ public: CVector3f AbsoluteScale() const; CAABox AABox(); CVector3f CenterPoint(); + u32 LightLayerIndex() const; bool MarkedVisible() const; bool IsMouseHovering() const; bool IsSelected() const; @@ -102,6 +105,7 @@ public: void SetRotation(const CQuaternion& rotation); void SetRotation(const CVector3f& rotEuler); void SetScale(const CVector3f& scale); + void SetLightLayerIndex(u32 index); void SetMouseHovering(bool Hovering); void SetSelected(bool Selected); void SetVisible(bool Visible); diff --git a/Scene/CScriptNode.cpp b/Scene/CScriptNode.cpp index 601fc676..1d3c088d 100644 --- a/Scene/CScriptNode.cpp +++ b/Scene/CScriptNode.cpp @@ -7,6 +7,7 @@ #include #include #include +#include CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObject *pObject) : CSceneNode(pScene, pParent) @@ -68,6 +69,10 @@ CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObje mpVolumePreviewNode->ForceAlphaEnabled(true); } } + + // Fetch LightParameters + mpLightParameters = new CLightParameters(mpInstance->LightParameters(), mpInstance->MasterTemplate()->GetGame()); + SetLightLayerIndex(mpLightParameters->LightLayerIndex()); } else @@ -155,11 +160,6 @@ void CScriptNode::Draw(ERenderOptions Options) LoadModelMatrix(); LoadLights(); - if (CGraphics::sLightMode == CGraphics::WorldLighting) - CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::sAreaAmbientColor.ToVector4f() * CGraphics::sWorldLightMultiplier; - else - CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); - // Default to drawing purple box if no model if (!mpActiveModel) { diff --git a/Scene/CScriptNode.h b/Scene/CScriptNode.h index 351d968e..cb8dcc3b 100644 --- a/Scene/CScriptNode.h +++ b/Scene/CScriptNode.h @@ -4,6 +4,7 @@ #include "CSceneNode.h" #include "CModelNode.h" #include "CCollisionNode.h" +#include #include class CScriptNode : public CSceneNode @@ -17,6 +18,8 @@ class CScriptNode : public CSceneNode bool mHasVolumePreview; CModelNode *mpVolumePreviewNode; + CLightParameters *mpLightParameters; + public: CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0); ENodeType NodeType(); diff --git a/Scene/CStaticNode.cpp b/Scene/CStaticNode.cpp index 44d91ca3..e9127756 100644 --- a/Scene/CStaticNode.cpp +++ b/Scene/CStaticNode.cpp @@ -46,7 +46,7 @@ void CStaticNode::Draw(ERenderOptions Options) { if (!mpModel) return; - CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(0, 0, 0, 1); + CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(0, 0, 0, 1); float Multiplier = CGraphics::sWorldLightMultiplier; CGraphics::sPixelBlock.TevColor = CVector4f(Multiplier,Multiplier,Multiplier,1); CGraphics::sNumLights = 0;