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[1].Load();
sDefaultDirectionalLights[2].Load();
UpdateLightBlock();
sVertexBlock.COLOR0_Amb = CColor::skGray.ToVector4f();
UpdateVertexBlock();
}
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 \
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 \

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,8 +44,10 @@ protected:
bool mVisible;
std::list<CSceneNode*> 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);

View File

@ -7,6 +7,7 @@
#include <Core/CRenderer.h>
#include <Core/CResCache.h>
#include <Core/CSceneManager.h>
#include <Resource/script/CMasterTemplate.h>
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)
{

View File

@ -4,6 +4,7 @@
#include "CSceneNode.h"
#include "CModelNode.h"
#include "CCollisionNode.h"
#include <Core/CLightParameters.h>
#include <Resource/script/CScriptObject.h>
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();

View File

@ -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;