Added support for dynamic lighting in Metroid Prime 3 + some other dynamic lighting additions and fixes

This commit is contained in:
parax0 2015-10-25 23:36:53 -06:00
parent b187da3925
commit 763d4b8b0a
14 changed files with 218 additions and 31 deletions

View File

@ -156,6 +156,10 @@ void CGraphics::SetDefaultLighting()
sDefaultDirectionalLights[0].Load(); sDefaultDirectionalLights[0].Load();
sDefaultDirectionalLights[1].Load(); sDefaultDirectionalLights[1].Load();
sDefaultDirectionalLights[2].Load(); sDefaultDirectionalLights[2].Load();
UpdateLightBlock();
sVertexBlock.COLOR0_Amb = CColor::skGray.ToVector4f();
UpdateVertexBlock();
} }
void CGraphics::SetIdentityMVP() void CGraphics::SetIdentityMVP()

25
Core/CLightParameters.cpp Normal file
View File

@ -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();
}

18
Core/CLightParameters.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef CLIGHTPARAMETERS_H
#define CLIGHTPARAMETERS_H
#include <Resource/CGameArea.h>
#include <Resource/script/CProperty.h>
class CLightParameters
{
CPropertyStruct *mpStruct;
EGame mGame;
public:
CLightParameters(CPropertyStruct *pStruct, EGame game);
~CLightParameters();
int LightLayerIndex();
};
#endif // CLIGHTPARAMETERS_H

View File

@ -152,7 +152,8 @@ SOURCES += \
Resource/CAnimationParameters.cpp \ Resource/CAnimationParameters.cpp \
UI/WAnimParamsEditor.cpp \ UI/WAnimParamsEditor.cpp \
Resource/CCollisionMeshGroup.cpp \ Resource/CCollisionMeshGroup.cpp \
Core/CFrustumPlanes.cpp Core/CFrustumPlanes.cpp \
Core/CLightParameters.cpp
HEADERS += \ HEADERS += \
Common/AnimUtil.h \ Common/AnimUtil.h \
@ -320,7 +321,8 @@ HEADERS += \
Resource/CAnimationParameters.h \ Resource/CAnimationParameters.h \
UI/WAnimParamsEditor.h \ UI/WAnimParamsEditor.h \
Resource/CCollisionMeshGroup.h \ Resource/CCollisionMeshGroup.h \
Core/CFrustumPlanes.h Core/CFrustumPlanes.h \
Core/CLightParameters.h
FORMS += \ FORMS += \
UI/CWorldEditorWindow.ui \ UI/CWorldEditorWindow.ui \

View File

@ -88,6 +88,11 @@ ELightType CLight::GetType() const
return mType; return mType;
} }
u32 CLight::GetLayerIndex() const
{
return mLayerIndex;
}
CVector3f CLight::GetPosition() const CVector3f CLight::GetPosition() const
{ {
return mPosition; return mPosition;
@ -136,6 +141,11 @@ float CLight::GetIntensity()
} }
// ************ SETTERS ************ // ************ SETTERS ************
void CLight::SetLayer(u32 index)
{
mLayerIndex = index;
}
void CLight::SetPosition(const CVector3f& Position) void CLight::SetPosition(const CVector3f& Position)
{ {
mPosition = Position; mPosition = Position;

View File

@ -23,12 +23,14 @@ enum ELightType
class CLight class CLight
{ {
ELightType mType; ELightType mType;
u32 mLayerIndex;
CVector3f mPosition; CVector3f mPosition;
CVector3f mDirection; CVector3f mDirection;
CColor mColor; CColor mColor;
float mSpotCutoff; float mSpotCutoff;
CVector3f mDistAttenCoefficients; CVector3f mDistAttenCoefficients;
CVector3f mAngleAttenCoefficients; CVector3f mAngleAttenCoefficients;
float mRadius; float mRadius;
float mIntensity; float mIntensity;
u8 mFlags; u8 mFlags;
@ -45,6 +47,7 @@ private:
public: public:
// Getters // Getters
ELightType GetType() const; ELightType GetType() const;
u32 GetLayerIndex() const;
CVector3f GetPosition() const; CVector3f GetPosition() const;
CVector3f GetDirection() const; CVector3f GetDirection() const;
CColor GetColor() const; CColor GetColor() const;
@ -54,6 +57,7 @@ public:
float GetIntensity(); float GetIntensity();
// Setters // Setters
void SetLayer(u32 index);
void SetPosition(const CVector3f& Position); void SetPosition(const CVector3f& Position);
void SetDirection(const CVector3f& Direction); void SetDirection(const CVector3f& Direction);
void SetColor(const CColor& Color); void SetColor(const CColor& Color);

View File

@ -198,6 +198,7 @@ void CAreaLoader::ReadLightsPrime()
1.f, 0.f, 0.f); 1.f, 0.f, 0.f);
} }
Light->SetLayer(ly);
mpArea->mLightLayers[ly][l] = Light; mpArea->mLightLayers[ly][l] = Light;
} }
} }
@ -336,7 +337,87 @@ void CAreaLoader::ReadGeometryCorruption()
std::cout << "\n"; 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 ************ // ************ COMMON ************
void CAreaLoader::ReadCompressedBlocks() void CAreaLoader::ReadCompressedBlocks()
@ -508,6 +589,7 @@ CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA)
Loader.ReadGeometryCorruption(); Loader.ReadGeometryCorruption();
Loader.ReadSCLYEchoes(); Loader.ReadSCLYEchoes();
Loader.ReadCollision(); Loader.ReadCollision();
if (Loader.mVersion == eCorruption) Loader.ReadLightsCorruption();
break; break;
default: default:
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + StringUtil::ToHexString(version)); Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + StringUtil::ToHexString(version));

View File

@ -67,6 +67,7 @@ class CAreaLoader
// Corruption // Corruption
void ReadHeaderCorruption(); void ReadHeaderCorruption();
void ReadGeometryCorruption(); void ReadGeometryCorruption();
void ReadLightsCorruption();
// Common // Common
void ReadCompressedBlocks(); void ReadCompressedBlocks();

View File

@ -45,7 +45,11 @@ void CLightNode::Draw(ERenderOptions)
CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(1.f); CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(1.f);
CGraphics::sVertexBlock.COLOR0_Mat = CVector4f(1.f); CGraphics::sVertexBlock.COLOR0_Mat = CVector4f(1.f);
CGraphics::UpdateVertexBlock(); 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 // Below commented-out code is for light radius visualization as a bounding box
/*float r = mLight->GetRadius(); /*float r = mLight->GetRadius();

View File

@ -26,6 +26,9 @@ CSceneNode::CSceneNode(CSceneManager *pScene, CSceneNode *pParent)
_mInheritsRotation = true; _mInheritsRotation = true;
_mInheritsScale = true; _mInheritsScale = true;
mLightLayerIndex = 0;
mLightCount = 0;
mMouseHovering = false; mMouseHovering = false;
mSelected = false; mSelected = false;
mVisible = true; mVisible = true;
@ -115,8 +118,10 @@ void CSceneNode::LoadModelMatrix()
void CSceneNode::BuildLightList(CGameArea *pArea) void CSceneNode::BuildLightList(CGameArea *pArea)
{ {
mLightCount = 0; mLightCount = 0;
mAmbientColor = CColor::skBlack;
u32 LayerCount = pArea->GetLightLayerCount(); u32 index = mLightLayerIndex;
if ((pArea->GetLightLayerCount() <= index) || (pArea->GetLightCount(index) == 0)) index = 0;
struct SLightEntry { struct SLightEntry {
CLight *pLight; CLight *pLight;
@ -131,20 +136,20 @@ void CSceneNode::BuildLightList(CGameArea *pArea)
}; };
std::vector<SLightEntry> LightEntries; std::vector<SLightEntry> 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)
CLight* pLight = pArea->GetLight(iLayer, iLight); mAmbientColor = pLight->GetColor();
// Directional/Spot lights take priority over custom lights. // Other lights will be used depending which are closest to the node
if ((pLight->GetType() == eDirectional) || (pLight->GetType() == eSpot)) else
LightEntries.push_back(SLightEntry(pLight, 0));
// Custom lights will be used depending which are closest to the node
else if (pLight->GetType() == eCustom)
{ {
bool IsInRange = AABox().IntersectsSphere(pLight->GetPosition(), pLight->GetRadius()); bool IsInRange = AABox().IntersectsSphere(pLight->GetPosition(), pLight->GetRadius());
@ -155,7 +160,6 @@ void CSceneNode::BuildLightList(CGameArea *pArea)
} }
} }
} }
}
// Determine which lights are closest // Determine which lights are closest
std::sort(LightEntries.begin(), LightEntries.end()); std::sort(LightEntries.begin(), LightEntries.end());
@ -169,13 +173,29 @@ void CSceneNode::LoadLights()
{ {
CGraphics::sNumLights = 0; CGraphics::sNumLights = 0;
if (CGraphics::sLightMode == CGraphics::BasicLighting) switch (CGraphics::sLightMode)
CGraphics::SetDefaultLighting(); {
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++) for (u32 iLight = 0; iLight < mLightCount; iLight++)
mLights[iLight]->Load(); mLights[iLight]->Load();
break;
}
CGraphics::UpdateLightBlock(); CGraphics::UpdateLightBlock();
} }
@ -340,6 +360,11 @@ CVector3f CSceneNode::CenterPoint()
return AABox().Center(); return AABox().Center();
} }
u32 CSceneNode::LightLayerIndex() const
{
return mLightLayerIndex;
}
bool CSceneNode::MarkedVisible() const bool CSceneNode::MarkedVisible() const
{ {
// The reason I have this function is because the instance view needs to know whether a node is marked // 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(); MarkTransformChanged();
} }
void CSceneNode::SetLightLayerIndex(u32 index)
{
mLightLayerIndex = index;
}
void CSceneNode::SetMouseHovering(bool Hovering) void CSceneNode::SetMouseHovering(bool Hovering)
{ {
mMouseHovering = Hovering; mMouseHovering = Hovering;

View File

@ -44,8 +44,10 @@ protected:
bool mVisible; bool mVisible;
std::list<CSceneNode*> mChildren; std::list<CSceneNode*> mChildren;
u32 mLightLayerIndex;
u32 mLightCount; u32 mLightCount;
CLight* mLights[8]; CLight* mLights[8];
CColor mAmbientColor;
public: public:
explicit CSceneNode(CSceneManager *pScene, CSceneNode *pParent = 0); explicit CSceneNode(CSceneManager *pScene, CSceneNode *pParent = 0);
@ -89,6 +91,7 @@ public:
CVector3f AbsoluteScale() const; CVector3f AbsoluteScale() const;
CAABox AABox(); CAABox AABox();
CVector3f CenterPoint(); CVector3f CenterPoint();
u32 LightLayerIndex() const;
bool MarkedVisible() const; bool MarkedVisible() const;
bool IsMouseHovering() const; bool IsMouseHovering() const;
bool IsSelected() const; bool IsSelected() const;
@ -102,6 +105,7 @@ public:
void SetRotation(const CQuaternion& rotation); void SetRotation(const CQuaternion& rotation);
void SetRotation(const CVector3f& rotEuler); void SetRotation(const CVector3f& rotEuler);
void SetScale(const CVector3f& scale); void SetScale(const CVector3f& scale);
void SetLightLayerIndex(u32 index);
void SetMouseHovering(bool Hovering); void SetMouseHovering(bool Hovering);
void SetSelected(bool Selected); void SetSelected(bool Selected);
void SetVisible(bool Visible); void SetVisible(bool Visible);

View File

@ -7,6 +7,7 @@
#include <Core/CRenderer.h> #include <Core/CRenderer.h>
#include <Core/CResCache.h> #include <Core/CResCache.h>
#include <Core/CSceneManager.h> #include <Core/CSceneManager.h>
#include <Resource/script/CMasterTemplate.h>
CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObject *pObject) CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObject *pObject)
: CSceneNode(pScene, pParent) : CSceneNode(pScene, pParent)
@ -68,6 +69,10 @@ CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObje
mpVolumePreviewNode->ForceAlphaEnabled(true); mpVolumePreviewNode->ForceAlphaEnabled(true);
} }
} }
// Fetch LightParameters
mpLightParameters = new CLightParameters(mpInstance->LightParameters(), mpInstance->MasterTemplate()->GetGame());
SetLightLayerIndex(mpLightParameters->LightLayerIndex());
} }
else else
@ -155,11 +160,6 @@ void CScriptNode::Draw(ERenderOptions Options)
LoadModelMatrix(); LoadModelMatrix();
LoadLights(); 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 // Default to drawing purple box if no model
if (!mpActiveModel) if (!mpActiveModel)
{ {

View File

@ -4,6 +4,7 @@
#include "CSceneNode.h" #include "CSceneNode.h"
#include "CModelNode.h" #include "CModelNode.h"
#include "CCollisionNode.h" #include "CCollisionNode.h"
#include <Core/CLightParameters.h>
#include <Resource/script/CScriptObject.h> #include <Resource/script/CScriptObject.h>
class CScriptNode : public CSceneNode class CScriptNode : public CSceneNode
@ -17,6 +18,8 @@ class CScriptNode : public CSceneNode
bool mHasVolumePreview; bool mHasVolumePreview;
CModelNode *mpVolumePreviewNode; CModelNode *mpVolumePreviewNode;
CLightParameters *mpLightParameters;
public: public:
CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0); CScriptNode(CSceneManager *pScene, CSceneNode *pParent = 0, CScriptObject *pObject = 0);
ENodeType NodeType(); ENodeType NodeType();