mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-25 16:51:38 +00:00
274 lines
9.0 KiB
C++
274 lines
9.0 KiB
C++
#include "CLight.h"
|
|
#include "Core/Render/CGraphics.h"
|
|
#include <Common/Common.h>
|
|
#include <cmath>
|
|
#include <float.h>
|
|
|
|
#define CLIGHT_NO_RADIUS 0x40
|
|
#define CLIGHT_NO_INTENSITY 0x80
|
|
|
|
CLight::CLight()
|
|
: mPosition(skDefaultLightPos)
|
|
, mDirection(skDefaultLightDir)
|
|
, mDistAttenCoefficients(0.f, 1.f, 0.f)
|
|
, mAngleAttenCoefficients(0.f, 1.f, 0.f)
|
|
, mCachedRadius(0.f)
|
|
, mCachedIntensity(0.f)
|
|
, mDirtyFlags(CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY)
|
|
{
|
|
}
|
|
|
|
// ************ DATA MANIPULATION ************
|
|
|
|
// This function is reverse engineered from the kiosk demo's code
|
|
float CLight::CalculateRadius() const
|
|
{
|
|
if ((mDistAttenCoefficients.Y >= FLT_EPSILON) ||
|
|
(mDistAttenCoefficients.Z >= FLT_EPSILON))
|
|
{
|
|
float Intensity = GetIntensity();
|
|
|
|
if (mDistAttenCoefficients.Z > FLT_EPSILON)
|
|
{
|
|
if (Intensity <= FLT_EPSILON)
|
|
return 0.f;
|
|
|
|
float IntensityMod = (Intensity * 5.f / 255.f * mDistAttenCoefficients.Z);
|
|
return sqrtf(Intensity / IntensityMod);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (mDistAttenCoefficients.Y <= FLT_EPSILON)
|
|
return 0.f;
|
|
|
|
float IntensityMod = (Intensity * 5.f) / 255.f;
|
|
if (IntensityMod < 0.2f)
|
|
IntensityMod = 0.2f;
|
|
|
|
return Intensity / (IntensityMod * mDistAttenCoefficients.Y);
|
|
}
|
|
}
|
|
|
|
else return 3000000000000000000000000000000000000.f;
|
|
}
|
|
|
|
// This function is also reverse engineered from the kiosk demo's code
|
|
float CLight::CalculateIntensity() const
|
|
{
|
|
// Get the color component with the greatest numeric value
|
|
float Greatest = (mColor.G >= mColor.B) ? mColor.G : mColor.B;
|
|
Greatest = (mColor.R >= Greatest) ? mColor.R : Greatest;
|
|
|
|
float Multiplier = (mType == eCustom) ? mAngleAttenCoefficients.X : 1.0f;
|
|
return Greatest * Multiplier;
|
|
}
|
|
|
|
// As is this one... partly
|
|
CVector3f CLight::CalculateSpotAngleAtten()
|
|
{
|
|
if (mType != eSpot) return CVector3f(1.f, 0.f, 0.f);
|
|
|
|
if ((mSpotCutoff < 0.f) || (mSpotCutoff > 90.f))
|
|
return CVector3f(1.f, 0.f, 0.f);
|
|
|
|
float RadianCutoff = (mSpotCutoff * 3.1415927f) / 180.f;
|
|
float RadianCosine = cosf(RadianCutoff);
|
|
float InvCosine = 1.f - RadianCosine;
|
|
|
|
return CVector3f(0.f, -RadianCosine / InvCosine, 1.f / InvCosine);
|
|
}
|
|
|
|
// ************ ACCESSORS ************
|
|
float CLight::GetRadius() const
|
|
{
|
|
if (mDirtyFlags & CLIGHT_NO_RADIUS)
|
|
{
|
|
mCachedRadius = CalculateRadius();
|
|
mDirtyFlags &= ~CLIGHT_NO_RADIUS;
|
|
}
|
|
|
|
return mCachedRadius * 2;
|
|
}
|
|
|
|
float CLight::GetIntensity() const
|
|
{
|
|
if (mDirtyFlags & CLIGHT_NO_INTENSITY)
|
|
{
|
|
mCachedIntensity = CalculateIntensity();
|
|
mDirtyFlags &= ~CLIGHT_NO_INTENSITY;
|
|
}
|
|
|
|
return mCachedIntensity;
|
|
}
|
|
|
|
void CLight::SetColor(const CColor& rkColor)
|
|
{
|
|
mColor = rkColor;
|
|
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
|
|
}
|
|
|
|
void CLight::SetSpotCutoff(float Cutoff)
|
|
{
|
|
mSpotCutoff = Cutoff * 0.5f;
|
|
CalculateSpotAngleAtten();
|
|
}
|
|
|
|
void CLight::SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC)
|
|
{
|
|
mDistAttenCoefficients.X = DistCoefA;
|
|
mDistAttenCoefficients.Y = DistCoefB;
|
|
mDistAttenCoefficients.Z = DistCoefC;
|
|
}
|
|
|
|
void CLight::SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC)
|
|
{
|
|
mAngleAttenCoefficients.X = AngleCoefA;
|
|
mAngleAttenCoefficients.Y = AngleCoefB;
|
|
mAngleAttenCoefficients.Z = AngleCoefC;
|
|
}
|
|
|
|
CStructProperty* CLight::GetProperties() const
|
|
{
|
|
//@todo MP1 properties only
|
|
//@todo we cannot display full properties because a lot of them are discarded on load
|
|
static CStructProperty* pProperties = nullptr;
|
|
|
|
if (!pProperties)
|
|
{
|
|
pProperties = (CStructProperty*) IProperty::CreateIntrinsic(EPropertyType::Struct,
|
|
EGame::Prime,
|
|
0,
|
|
"Light");
|
|
|
|
CChoiceProperty* pLightType = (CChoiceProperty*) IProperty::CreateIntrinsic(EPropertyType::Choice,
|
|
pProperties,
|
|
MEMBER_OFFSET(CLight, mType),
|
|
"LightType");
|
|
pLightType->AddValue("LocalAmbient", eLocalAmbient);
|
|
pLightType->AddValue("Directional", eDirectional);
|
|
pLightType->AddValue("Spot", eSpot);
|
|
pLightType->AddValue("Custom", eCustom);
|
|
|
|
IProperty::CreateIntrinsic(EPropertyType::Color,
|
|
pProperties,
|
|
MEMBER_OFFSET(CLight, mColor),
|
|
"Color");
|
|
|
|
IProperty::CreateIntrinsic(EPropertyType::Vector,
|
|
pProperties,
|
|
MEMBER_OFFSET(CLight, mPosition),
|
|
"Position");
|
|
|
|
IProperty::CreateIntrinsic(EPropertyType::Vector,
|
|
pProperties,
|
|
MEMBER_OFFSET(CLight, mDirection),
|
|
"Direction");
|
|
|
|
IProperty::CreateIntrinsic(EPropertyType::Float,
|
|
pProperties,
|
|
MEMBER_OFFSET(CLight, mSpotCutoff),
|
|
"SpotCutoff");
|
|
}
|
|
|
|
return pProperties;
|
|
}
|
|
|
|
// ************ OTHER ************
|
|
void CLight::Load() const
|
|
{
|
|
u8 Index = (u8) CGraphics::sNumLights;
|
|
if (Index >= 8) return;
|
|
|
|
CGraphics::SLightBlock::SGXLight *pLight = &CGraphics::sLightBlock.Lights[Index];
|
|
|
|
switch (mType)
|
|
{
|
|
case eLocalAmbient:
|
|
// LocalAmbient is already accounted for in CGraphics::sAreaAmbientColor
|
|
return;
|
|
case eDirectional:
|
|
pLight->Position = CVector4f(-mDirection * 1048576.f, 1.f);
|
|
pLight->Direction = CVector4f(mDirection, 0.f);
|
|
pLight->Color = mColor * CGraphics::sWorldLightMultiplier;
|
|
pLight->DistAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
|
pLight->AngleAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
|
break;
|
|
case eSpot:
|
|
pLight->Position = CVector4f(mPosition, 1.f);
|
|
pLight->Direction = CVector4f(mDirection, 0.f);
|
|
pLight->Color = mColor * CGraphics::sWorldLightMultiplier;
|
|
pLight->DistAtten = mDistAttenCoefficients;
|
|
pLight->AngleAtten = mAngleAttenCoefficients;
|
|
break;
|
|
case eCustom:
|
|
pLight->Position = CVector4f(mPosition, 1.f);
|
|
pLight->Direction = CVector4f(mDirection, 0.f);
|
|
pLight->Color = mColor * CGraphics::sWorldLightMultiplier;
|
|
pLight->DistAtten = mDistAttenCoefficients;
|
|
pLight->AngleAtten = mAngleAttenCoefficients;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
CGraphics::sNumLights++;
|
|
}
|
|
|
|
// ************ STATIC ************
|
|
CLight* CLight::BuildLocalAmbient(const CVector3f& rkPosition, const CColor& rkColor)
|
|
{
|
|
CLight *pLight = new CLight;
|
|
pLight->mType = eLocalAmbient;
|
|
pLight->mPosition = rkPosition;
|
|
pLight->mDirection = skDefaultLightDir;
|
|
pLight->mColor = rkColor;
|
|
pLight->mSpotCutoff = 0.f;
|
|
return pLight;
|
|
}
|
|
|
|
CLight* CLight::BuildDirectional(const CVector3f& rkPosition, const CVector3f& rkDirection, const CColor& rkColor)
|
|
{
|
|
CLight *pLight = new CLight;
|
|
pLight->mType = eDirectional;
|
|
pLight->mPosition = rkPosition;
|
|
pLight->mDirection = rkDirection;
|
|
pLight->mColor = rkColor;
|
|
pLight->mSpotCutoff = 0.f;
|
|
return pLight;
|
|
}
|
|
|
|
CLight* CLight::BuildSpot(const CVector3f& rkPosition, const CVector3f& rkDirection, const CColor& rkColor, float Cutoff)
|
|
{
|
|
CLight *pLight = new CLight;
|
|
pLight->mType = eSpot;
|
|
pLight->mPosition = rkPosition;
|
|
pLight->mDirection = -rkDirection.Normalized();
|
|
pLight->mColor = rkColor;
|
|
pLight->mSpotCutoff = Cutoff * 0.5f;
|
|
pLight->mAngleAttenCoefficients = pLight->CalculateSpotAngleAtten();
|
|
return pLight;
|
|
}
|
|
|
|
CLight* CLight::BuildCustom(const CVector3f& rkPosition, const CVector3f& rkDirection, const CColor& rkColor,
|
|
float DistAttenA, float DistAttenB, float DistAttenC,
|
|
float AngleAttenA, float AngleAttenB, float AngleAttenC)
|
|
{
|
|
CLight *pLight = new CLight;
|
|
pLight->mType = eCustom;
|
|
pLight->mPosition = rkPosition;
|
|
pLight->mDirection = rkDirection;
|
|
pLight->mColor = rkColor;
|
|
pLight->mSpotCutoff = 0.f;
|
|
pLight->mDistAttenCoefficients.X = DistAttenA;
|
|
pLight->mDistAttenCoefficients.Y = DistAttenB;
|
|
pLight->mDistAttenCoefficients.Z = DistAttenC;
|
|
pLight->mAngleAttenCoefficients.X = AngleAttenA;
|
|
pLight->mAngleAttenCoefficients.Y = AngleAttenB;
|
|
pLight->mAngleAttenCoefficients.Z = AngleAttenC * AngleAttenC;
|
|
return pLight;
|
|
}
|
|
|
|
// ************ CONSTANTS ************
|
|
const CVector3f CLight::skDefaultLightPos(0.f, 0.f, 0.f);
|
|
const CVector3f CLight::skDefaultLightDir(0.f,-1.f, 0.f);
|