#include "CLight.h" #include "Core/Render/CGraphics.h" #include #include #include #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);