2020-01-15 12:07:48 +00:00
|
|
|
#include "Runtime/Character/CActorLights.hpp"
|
|
|
|
|
|
|
|
#include "Runtime/CStateManager.hpp"
|
|
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
|
|
#include "Runtime/Camera/CFirstPersonCamera.hpp"
|
|
|
|
#include "Runtime/Collision/CGameCollision.hpp"
|
2022-02-25 07:45:25 +00:00
|
|
|
#include "Runtime/Graphics/CCubeRenderer.hpp"
|
2020-01-15 12:07:48 +00:00
|
|
|
#include "Runtime/Graphics/CModel.hpp"
|
|
|
|
#include "Runtime/World/CExplosion.hpp"
|
|
|
|
#include "Runtime/World/CGameArea.hpp"
|
|
|
|
#include "Runtime/World/CGameLight.hpp"
|
|
|
|
|
2019-09-21 13:07:13 +00:00
|
|
|
#include "TCastTo.hpp" // Generated file, do not modify include path
|
2016-08-14 21:11:44 +00:00
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2016-08-14 21:11:44 +00:00
|
|
|
|
2016-08-31 00:33:59 +00:00
|
|
|
s32 CActorLights::sFrameSchedulerCount = 0;
|
2018-12-08 05:30:43 +00:00
|
|
|
CActorLights::CActorLights(u32 areaUpdateFramePeriod, const zeus::CVector3f& actorPosBias, int maxDynamicLights,
|
|
|
|
int maxAreaLights, bool ambientChannelOverflow, bool layer2, bool disableWorldLights,
|
|
|
|
float positionUpdateThreshold)
|
2020-04-21 07:22:41 +00:00
|
|
|
: x298_28_inArea(!disableWorldLights && maxAreaLights > 0)
|
2020-04-10 19:03:15 +00:00
|
|
|
, x298_29_ambienceGenerated(ambientChannelOverflow)
|
|
|
|
, x298_30_layer2(layer2)
|
|
|
|
, x298_31_disableWorldLights(disableWorldLights)
|
|
|
|
, x2a8_areaUpdateFramePeriod(areaUpdateFramePeriod)
|
2018-12-08 05:30:43 +00:00
|
|
|
, x2ac_actorPosBias(actorPosBias)
|
|
|
|
, x2b8_maxAreaLights(maxAreaLights)
|
|
|
|
, x2bc_maxDynamicLights(maxDynamicLights)
|
|
|
|
, x2cc_actorPositionDeltaUpdateThreshold(positionUpdateThreshold * positionUpdateThreshold) {
|
|
|
|
sFrameSchedulerCount++;
|
|
|
|
sFrameSchedulerCount &= 7;
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::BuildConstantAmbientLighting() {
|
|
|
|
x299_26_ambientOnly = true;
|
|
|
|
x298_24_dirty = true;
|
|
|
|
x29c_shadowLightArrIdx = -1;
|
|
|
|
x2a0_shadowLightIdx = -1;
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::BuildConstantAmbientLighting(const zeus::CColor& color) {
|
|
|
|
x299_26_ambientOnly = false;
|
|
|
|
x288_ambientColor = color;
|
|
|
|
x294_aid = kInvalidAreaId;
|
|
|
|
x298_24_dirty = true;
|
|
|
|
x298_26_hasAreaLights = true;
|
|
|
|
x29c_shadowLightArrIdx = -1;
|
|
|
|
x2a0_shadowLightIdx = -1;
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::BuildFakeLightList(const std::vector<CLight>& lights, const zeus::CColor& color) {
|
|
|
|
BuildConstantAmbientLighting(color);
|
|
|
|
x0_areaLights.clear();
|
|
|
|
x144_dynamicLights = lights;
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::BuildFaceLightList(const CStateManager& mgr, const CGameArea& area, const zeus::CAABox& aabb) {
|
|
|
|
zeus::CTransform fpTransform = mgr.GetCameraManager()->GetFirstPersonCamera()->GetTransform();
|
2019-02-25 08:14:59 +00:00
|
|
|
x298_26_hasAreaLights = true;
|
2019-02-24 07:15:54 +00:00
|
|
|
x288_ambientColor = zeus::skBlack;
|
2018-12-08 05:30:43 +00:00
|
|
|
x144_dynamicLights.clear();
|
2019-02-24 07:15:54 +00:00
|
|
|
zeus::CColor accumColor = zeus::skBlack;
|
2018-12-08 05:30:43 +00:00
|
|
|
for (CEntity* light : mgr.GetLightObjectList()) {
|
|
|
|
if (!light || !light->GetActive())
|
|
|
|
continue;
|
|
|
|
CGameLight* castLight = static_cast<CGameLight*>(light);
|
|
|
|
if (TCastToConstPtr<CExplosion> explosion = mgr.GetObjectById(castLight->GetParentId())) {
|
|
|
|
CLight originalLight = castLight->GetLight();
|
|
|
|
CLight explosionLight = originalLight;
|
|
|
|
explosionLight.SetAttenuation(
|
|
|
|
explosionLight.GetAttenuationConstant() * g_tweakGui->GetExplosionLightFalloffMultConstant(),
|
|
|
|
explosionLight.GetAttenuationLinear() * g_tweakGui->GetExplosionLightFalloffMultLinear(),
|
|
|
|
explosionLight.GetAttenuationQuadratic() * g_tweakGui->GetExplosionLightFalloffMultQuadratic());
|
|
|
|
zeus::CVector3f camToExplo = explosion->GetTranslation() - fpTransform.origin;
|
2019-02-24 07:15:54 +00:00
|
|
|
if (fpTransform.transposeRotate(camToExplo).dot(zeus::skForward) >= 0.f) {
|
2018-12-08 05:30:43 +00:00
|
|
|
camToExplo.y() = -camToExplo.y() + ITweakGui::FaceReflectionDistanceDebugValueToActualValue(
|
|
|
|
g_tweakGui->GetFaceReflectionDistance());
|
|
|
|
camToExplo.z() = -camToExplo.z() +
|
|
|
|
ITweakGui::FaceReflectionHeightDebugValueToActualValue(g_tweakGui->GetFaceReflectionHeight());
|
|
|
|
explosionLight.SetPosition(fpTransform * camToExplo);
|
|
|
|
zeus::CSphere sphere(originalLight.GetPosition(), originalLight.GetRadius());
|
|
|
|
if (aabb.intersects(sphere)) {
|
|
|
|
accumColor += explosionLight.GetNormalIndependentLightingAtPoint(fpTransform.origin);
|
|
|
|
if (originalLight.GetIntensity() > FLT_EPSILON && originalLight.GetRadius() > FLT_EPSILON)
|
|
|
|
x144_dynamicLights.push_back(explosionLight);
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2017-04-13 19:28:31 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
float greyscale = accumColor.rgbDot(zeus::CColor(0.3f, 0.6f, 0.1f));
|
|
|
|
if (greyscale < 0.012f)
|
|
|
|
x144_dynamicLights.clear();
|
|
|
|
|
|
|
|
if (greyscale > 0.03f) {
|
|
|
|
float attMul = 1.f / (0.03f / greyscale);
|
|
|
|
for (CLight& light : x144_dynamicLights)
|
|
|
|
light.SetAttenuation(light.GetAttenuationConstant() * attMul, light.GetAttenuationLinear() * attMul,
|
|
|
|
light.GetAttenuationQuadratic() * attMul);
|
|
|
|
}
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
struct SLightValue {
|
|
|
|
u32 x0_areaLightIdx;
|
|
|
|
zeus::CColor x4_color;
|
|
|
|
float x10_colorMag;
|
|
|
|
float x14_accumulatedMag = 0.f;
|
|
|
|
EPVSVisSetState x18_visiblity;
|
2017-04-10 06:57:00 +00:00
|
|
|
};
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::MergeOverflowLight(CLight& out, zeus::CColor& color, const CLight& in, float colorMag) {
|
|
|
|
color += in.GetColor() * colorMag;
|
|
|
|
out.SetAngleAttenuation(in.GetAngleAttenuationConstant() * colorMag + out.GetAngleAttenuationConstant(),
|
|
|
|
in.GetAngleAttenuationLinear() * colorMag + out.GetAngleAttenuationLinear(),
|
|
|
|
in.GetAngleAttenuationQuadratic() * colorMag + out.GetAngleAttenuationQuadratic());
|
|
|
|
out.SetAttenuation(in.GetAttenuationConstant() * colorMag + out.GetAttenuationConstant(),
|
|
|
|
in.GetAttenuationLinear() * colorMag + out.GetAttenuationLinear(),
|
|
|
|
in.GetAttenuationQuadratic() * colorMag + out.GetAttenuationQuadratic());
|
|
|
|
out.SetPosition(in.GetPosition() * colorMag + out.GetPosition());
|
|
|
|
out.SetDirection(in.GetDirection() * colorMag + out.GetDirection());
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::AddOverflowToLights(const CLight& light, const zeus::CColor& color, float mag) {
|
|
|
|
if (mag < 0.001f || x2b8_maxAreaLights < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mag = 1.f / mag;
|
|
|
|
zeus::CColor useColor = color * mag;
|
|
|
|
useColor.a() = 1.f;
|
|
|
|
x0_areaLights.push_back(
|
|
|
|
CLight::BuildCustom(light.GetPosition() * mag, light.GetDirection() * mag, useColor,
|
|
|
|
light.GetAttenuationConstant() * mag, light.GetAttenuationLinear() * mag,
|
|
|
|
light.GetAttenuationQuadratic() * mag, light.GetAngleAttenuationConstant() * mag,
|
|
|
|
light.GetAngleAttenuationLinear() * mag, light.GetAngleAttenuationQuadratic() * mag));
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::MoveAmbienceToLights(const zeus::CColor& color) {
|
|
|
|
if (x298_29_ambienceGenerated) {
|
|
|
|
x288_ambientColor += color * 0.333333f;
|
|
|
|
x288_ambientColor.a() = 1.f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
zeus::CColor useColor = x0_areaLights[0].GetColor() + color;
|
|
|
|
float maxComponent = std::max(useColor.r(), std::max(useColor.g(), useColor.b()));
|
|
|
|
if (maxComponent > FLT_EPSILON)
|
|
|
|
useColor *= (1.f / maxComponent);
|
|
|
|
useColor.a() = 1.f;
|
|
|
|
x0_areaLights[0].SetColor(useColor);
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::MultiplyLightingLevels(float level) {
|
|
|
|
x288_ambientColor *= level;
|
|
|
|
for (CLight& light : x0_areaLights) {
|
|
|
|
zeus::CColor color = light.GetColor();
|
|
|
|
color *= level;
|
|
|
|
color.a() = 1.f;
|
|
|
|
light.SetColor(color);
|
|
|
|
}
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2017-04-10 06:57:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::UpdateBrightLight() {
|
|
|
|
if (x2dc_brightLightLag > 0 && x299_24_inBrightLight)
|
|
|
|
--x2dc_brightLightLag;
|
|
|
|
else if (x2dc_brightLightLag < 15 && !x299_24_inBrightLight)
|
|
|
|
++x2dc_brightLightLag;
|
|
|
|
x299_25_useBrightLightLag = true;
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CActorLights::BuildAreaLightList(const CStateManager& mgr, const CGameArea& area, const zeus::CAABox& aabb) {
|
|
|
|
const std::vector<CWorldLight>& lightList =
|
|
|
|
x298_30_layer2 ? area.GetPostConstructed()->x80_lightsB : area.GetPostConstructed()->x60_lightsA;
|
|
|
|
const std::vector<CLight>& gfxLightList =
|
|
|
|
x298_30_layer2 ? area.GetPostConstructed()->x90_gfxLightsB : area.GetPostConstructed()->x70_gfxLightsA;
|
|
|
|
float worldLightingLevel = area.GetPostConstructed()->x1128_worldLightingLevel;
|
|
|
|
x298_26_hasAreaLights = lightList.size() != 0;
|
|
|
|
if (!x298_26_hasAreaLights || !x298_28_inArea) {
|
|
|
|
/* World lights disabled */
|
|
|
|
if (x298_31_disableWorldLights)
|
|
|
|
x2d4_worldLightingLevel = worldLightingLevel;
|
2017-08-31 02:42:37 +00:00
|
|
|
x29c_shadowLightArrIdx = -1;
|
2018-12-08 05:30:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
zeus::CVector3f vec;
|
2020-10-21 22:55:45 +00:00
|
|
|
if (!x298_24_dirty && x294_aid == area.GetAreaId()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
/* Early return if not ready for update */
|
|
|
|
if (mgr.GetInputFrameIdx() - x2a4_lastUpdateFrame < x2a8_areaUpdateFramePeriod)
|
|
|
|
return false;
|
|
|
|
vec = aabb.center() + x2ac_actorPosBias;
|
|
|
|
if (x2d4_worldLightingLevel == worldLightingLevel)
|
|
|
|
if ((x2c0_lastActorPos - vec).magSquared() < x2cc_actorPositionDeltaUpdateThreshold)
|
|
|
|
return false;
|
|
|
|
x2c0_lastActorPos = vec;
|
|
|
|
} else {
|
2020-10-21 22:55:45 +00:00
|
|
|
if (x294_aid != area.GetAreaId())
|
2018-12-08 05:30:43 +00:00
|
|
|
x2d8_brightLightIdx = -1;
|
|
|
|
x2a4_lastUpdateFrame = sFrameSchedulerCount + mgr.GetInputFrameIdx();
|
|
|
|
vec = aabb.center() + x2ac_actorPosBias;
|
|
|
|
x2c0_lastActorPos = vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset lighting state */
|
|
|
|
x2d4_worldLightingLevel = worldLightingLevel;
|
|
|
|
x298_24_dirty = false;
|
2020-10-21 22:55:45 +00:00
|
|
|
x294_aid = area.GetAreaId();
|
2018-12-08 05:30:43 +00:00
|
|
|
x29c_shadowLightArrIdx = -1;
|
2019-02-24 07:15:54 +00:00
|
|
|
x288_ambientColor = zeus::skClear;
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
/* Find candidate lights via PVS */
|
|
|
|
bool use2ndLayer;
|
|
|
|
if (x298_30_layer2) {
|
2017-04-10 06:57:00 +00:00
|
|
|
if (const CPVSAreaSet* pvs = area.GetAreaVisSet())
|
2018-12-08 05:30:43 +00:00
|
|
|
use2ndLayer = pvs->Has2ndLayerLights();
|
|
|
|
else
|
|
|
|
use2ndLayer = true;
|
|
|
|
} else {
|
|
|
|
use2ndLayer = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPVSVisSet sets[3];
|
|
|
|
sets[0].Reset(EPVSVisSetState::OutOfBounds);
|
|
|
|
sets[1].Reset(EPVSVisSetState::OutOfBounds);
|
|
|
|
sets[2].Reset(EPVSVisSetState::OutOfBounds);
|
|
|
|
|
|
|
|
if (const CPVSAreaSet* pvs = area.GetAreaVisSet()) {
|
|
|
|
zeus::CVector3f localVec = area.GetInverseTransform() * vec;
|
|
|
|
sets[0].SetTestPoint(pvs->GetVisOctree(), localVec);
|
|
|
|
localVec = area.GetInverseTransform() * aabb.max;
|
|
|
|
sets[1].SetTestPoint(pvs->GetVisOctree(), localVec);
|
|
|
|
localVec = area.GetInverseTransform() * aabb.min;
|
|
|
|
sets[2].SetTestPoint(pvs->GetVisOctree(), localVec);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<SLightValue> valList;
|
|
|
|
valList.reserve(lightList.size());
|
|
|
|
|
|
|
|
auto lightIt = lightList.begin();
|
|
|
|
int lightIdx = 0;
|
|
|
|
for (const CLight& light : gfxLightList) {
|
|
|
|
if (light.GetType() == ELightType::LocalAmbient) {
|
|
|
|
/* Take ambient here */
|
|
|
|
x288_ambientColor = light.GetNormalIndependentLightingAtPoint(vec);
|
|
|
|
} else {
|
|
|
|
EPVSVisSetState visible = EPVSVisSetState::OutOfBounds;
|
|
|
|
if (area.GetAreaVisSet()) {
|
|
|
|
if (lightIt->DoesCastShadows()) {
|
|
|
|
u32 pvsIdx;
|
|
|
|
if (use2ndLayer)
|
|
|
|
pvsIdx = area.Get2ndPVSLightFeature(lightIdx);
|
|
|
|
else
|
|
|
|
pvsIdx = area.Get1stPVSLightFeature(lightIdx);
|
|
|
|
visible = sets[0].GetVisible(pvsIdx);
|
|
|
|
if (visible != EPVSVisSetState::OutOfBounds)
|
|
|
|
visible = std::max(visible, sets[1].GetVisible(pvsIdx));
|
|
|
|
if (visible != EPVSVisSetState::OutOfBounds)
|
|
|
|
visible = std::max(visible, sets[2].GetVisible(pvsIdx));
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
if (visible != EPVSVisSetState::EndOfTree) {
|
|
|
|
zeus::CSphere sphere(light.GetPosition(), light.GetRadius() * 2.f);
|
|
|
|
if (aabb.intersects(sphere)) {
|
|
|
|
/* Light passes as candidate */
|
2020-03-13 20:32:24 +00:00
|
|
|
SLightValue& value = valList.emplace_back();
|
2018-12-08 05:30:43 +00:00
|
|
|
value.x0_areaLightIdx = lightIdx;
|
|
|
|
value.x4_color = light.GetNormalIndependentLightingAtPoint(vec);
|
|
|
|
value.x4_color.a() = 0.f;
|
|
|
|
value.x10_colorMag = value.x4_color.magnitude();
|
|
|
|
value.x18_visiblity = visible;
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
++lightIt;
|
|
|
|
++lightIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sort lights most intense to least intense */
|
|
|
|
std::sort(valList.begin(), valList.end(),
|
|
|
|
[](const SLightValue& a, const SLightValue& b) { return a.x10_colorMag > b.x10_colorMag; });
|
|
|
|
|
|
|
|
if (x298_27_findShadowLight) {
|
|
|
|
/* Accumulate magnitudes up to most intense for shadow dynamic range check */
|
|
|
|
x288_ambientColor.a() = 0.f;
|
|
|
|
float mag = x288_ambientColor.magnitude();
|
|
|
|
for (auto it = valList.rbegin(); it != valList.rend(); ++it) {
|
|
|
|
mag += it->x10_colorMag;
|
|
|
|
it->x14_accumulatedMag = mag;
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ambient color for overflow area lights */
|
2021-05-16 19:23:13 +00:00
|
|
|
zeus::CColor overflowAmbColor = zeus::skClear;
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
/* Averaged light for overflow area lights */
|
2021-06-07 19:29:18 +00:00
|
|
|
CLight overflowLight =
|
|
|
|
CLight::BuildCustom(zeus::skZero3f, zeus::skZero3f, zeus::skBlack, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
2021-05-16 19:23:13 +00:00
|
|
|
zeus::CColor overflowLightColor = zeus::skClear;
|
2018-12-08 05:30:43 +00:00
|
|
|
float overflowMag = 0.f;
|
|
|
|
|
|
|
|
/* Max significant lights */
|
|
|
|
int maxAreaLights = !x298_29_ambienceGenerated ? x2b8_maxAreaLights - 1 : x2b8_maxAreaLights;
|
|
|
|
x0_areaLights.clear();
|
|
|
|
|
|
|
|
/* Filter for performing final light visibility test */
|
2020-05-07 18:08:29 +00:00
|
|
|
constexpr auto filter = CMaterialFilter::MakeIncludeExclude(
|
2018-12-08 05:30:43 +00:00
|
|
|
CMaterialList(EMaterialTypes::Solid),
|
|
|
|
CMaterialList(EMaterialTypes::Projectile, EMaterialTypes::ProjectilePassthrough, EMaterialTypes::SeeThrough));
|
|
|
|
u32 mostSigLightIdx = 0;
|
|
|
|
|
|
|
|
/* Narrowphase test candidates starting with most intense */
|
2019-06-12 02:05:17 +00:00
|
|
|
for (size_t i = 0; i < valList.size(); ++i) {
|
2018-12-08 05:30:43 +00:00
|
|
|
const SLightValue& value = valList[i];
|
|
|
|
const CLight& light = gfxLightList[value.x0_areaLightIdx];
|
|
|
|
if (x0_areaLights.size() < maxAreaLights) {
|
|
|
|
/* Significant light */
|
|
|
|
bool actorToLightContact = true;
|
|
|
|
bool castShadows = lightList[value.x0_areaLightIdx].DoesCastShadows() && x298_25_castShadows;
|
|
|
|
bool outOfBounds = area.GetAreaVisSet() && value.x18_visiblity == EPVSVisSetState::OutOfBounds;
|
|
|
|
if (castShadows) {
|
|
|
|
/* Process shadow cast */
|
|
|
|
zeus::CVector3f delta = light.GetPosition() - vec;
|
|
|
|
float deltaMag = delta.magnitude();
|
|
|
|
bool useShadow = false;
|
|
|
|
if (x298_27_findShadowLight && x29c_shadowLightArrIdx == -1 && light.GetType() != ELightType::LocalAmbient &&
|
|
|
|
deltaMag > 2.f && !aabb.pointInside(light.GetPosition())) {
|
|
|
|
/* Perform shadow dynamic range check */
|
|
|
|
if (!x0_areaLights.size() ||
|
|
|
|
(x0_areaLights.size() == 1 && value.x10_colorMag / valList[mostSigLightIdx].x10_colorMag > 0.5f)) {
|
|
|
|
useShadow = value.x10_colorMag / value.x14_accumulatedMag >
|
|
|
|
x2d0_shadowDynamicRangeThreshold / (1.f + x2d0_shadowDynamicRangeThreshold);
|
|
|
|
}
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
if (useShadow) {
|
|
|
|
/* Note shadow light */
|
|
|
|
x29c_shadowLightArrIdx = x0_areaLights.size();
|
|
|
|
x2a0_shadowLightIdx = value.x0_areaLightIdx;
|
|
|
|
} else if (!outOfBounds) {
|
|
|
|
/* Note brightest light contact */
|
|
|
|
delta = delta * 1.f / deltaMag;
|
|
|
|
actorToLightContact = CGameCollision::RayStaticIntersectionArea(area, vec, delta, deltaMag, filter);
|
|
|
|
if (i == 0) {
|
|
|
|
x299_24_inBrightLight = actorToLightContact;
|
|
|
|
if (x2d8_brightLightIdx != value.x0_areaLightIdx) {
|
|
|
|
x2dc_brightLightLag = actorToLightContact ? 0 : 15;
|
|
|
|
x2d8_brightLightIdx = value.x0_areaLightIdx;
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
x299_25_useBrightLightLag = false;
|
|
|
|
actorToLightContact = true;
|
|
|
|
}
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
if (actorToLightContact) {
|
|
|
|
/* Add to final list */
|
|
|
|
if (x0_areaLights.size() == 0)
|
|
|
|
mostSigLightIdx = i;
|
|
|
|
x0_areaLights.push_back(light);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Overflow light */
|
|
|
|
if (!x298_29_ambienceGenerated && value.x10_colorMag > 0.001f) {
|
|
|
|
/* Average parameters into final light */
|
|
|
|
MergeOverflowLight(overflowLight, overflowLightColor, light, value.x10_colorMag);
|
|
|
|
overflowMag += value.x10_colorMag;
|
|
|
|
} else {
|
|
|
|
/* Average color into ambient channel */
|
|
|
|
overflowAmbColor += value.x4_color;
|
|
|
|
}
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Finalize overflow lights */
|
|
|
|
if (!x298_29_ambienceGenerated)
|
|
|
|
AddOverflowToLights(overflowLight, overflowLightColor, overflowMag);
|
|
|
|
else
|
|
|
|
MoveAmbienceToLights(overflowAmbColor);
|
|
|
|
|
|
|
|
/* Clamp ambient color */
|
|
|
|
if (x288_ambientColor.r() > 1.f)
|
|
|
|
x288_ambientColor.r() = 1.f;
|
|
|
|
if (x288_ambientColor.g() > 1.f)
|
|
|
|
x288_ambientColor.g() = 1.f;
|
|
|
|
if (x288_ambientColor.b() > 1.f)
|
|
|
|
x288_ambientColor.b() = 1.f;
|
|
|
|
x288_ambientColor.a() = 1.f;
|
|
|
|
|
|
|
|
/* Multiply down lighting with world fader level */
|
|
|
|
if (worldLightingLevel < 1.f)
|
|
|
|
MultiplyLightingLevels(worldLightingLevel);
|
|
|
|
|
|
|
|
return true;
|
2017-04-10 06:57:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::BuildDynamicLightList(const CStateManager& mgr, const zeus::CAABox& aabb) {
|
|
|
|
UpdateBrightLight();
|
|
|
|
x299_26_ambientOnly = false;
|
|
|
|
x144_dynamicLights.clear();
|
|
|
|
|
|
|
|
if (!x29a_findNearestDynamicLights) {
|
|
|
|
for (const CLight& light : mgr.GetDynamicLightList()) {
|
|
|
|
zeus::CSphere sphere(light.GetPosition(), light.GetRadius());
|
|
|
|
if (aabb.intersects(sphere))
|
|
|
|
x144_dynamicLights.push_back(light);
|
|
|
|
if (x144_dynamicLights.size() >= x2bc_maxDynamicLights)
|
|
|
|
break;
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
} else {
|
|
|
|
const CLight* addedLights[8] = {};
|
|
|
|
for (int i = 0; i < x2bc_maxDynamicLights && i < 8; ++i) {
|
|
|
|
float minRad = FLT_MAX;
|
|
|
|
for (const CLight& light : mgr.GetDynamicLightList()) {
|
|
|
|
zeus::CSphere sphere(light.GetPosition(), light.GetRadius());
|
|
|
|
float intRadius = aabb.intersectionRadius(sphere);
|
|
|
|
if (intRadius >= 0.f && intRadius < minRad) {
|
|
|
|
bool alreadyIn = false;
|
|
|
|
for (int j = 0; j < i; ++j) {
|
|
|
|
if (&light == addedLights[j]) {
|
|
|
|
alreadyIn = true;
|
|
|
|
break;
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
if (alreadyIn)
|
|
|
|
continue;
|
|
|
|
addedLights[i] = &light;
|
|
|
|
minRad = intRadius;
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
if (addedLights[i])
|
|
|
|
x144_dynamicLights.push_back(*addedLights[i]);
|
|
|
|
if (x144_dynamicLights.size() >= x2bc_maxDynamicLights)
|
|
|
|
break;
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
std::vector<CLight> CActorLights::BuildLightVector() const {
|
|
|
|
std::vector<CLight> lights;
|
2016-08-21 00:04:50 +00:00
|
|
|
|
2022-02-25 07:45:25 +00:00
|
|
|
if (!x0_areaLights.empty()) {
|
|
|
|
if (x2dc_brightLightLag != 0 && x299_25_useBrightLightLag) {
|
2018-12-08 05:30:43 +00:00
|
|
|
CLight overrideLight = x0_areaLights[0];
|
|
|
|
overrideLight.SetColor(overrideLight.GetColor() * (1.f - x2dc_brightLightLag / 15.f));
|
|
|
|
lights.push_back(overrideLight);
|
2022-02-25 07:45:25 +00:00
|
|
|
} else {
|
2018-12-08 05:30:43 +00:00
|
|
|
lights.push_back(x0_areaLights[0]);
|
2022-02-25 07:45:25 +00:00
|
|
|
}
|
2017-10-01 04:26:46 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
for (auto it = x0_areaLights.begin() + 1; it != x0_areaLights.end(); ++it) {
|
|
|
|
lights.push_back(*it);
|
|
|
|
}
|
|
|
|
}
|
2016-08-21 00:04:50 +00:00
|
|
|
|
2022-02-25 07:45:25 +00:00
|
|
|
for (const CLight& light : x144_dynamicLights) {
|
2018-12-08 05:30:43 +00:00
|
|
|
lights.push_back(light);
|
2022-02-25 07:45:25 +00:00
|
|
|
}
|
2018-01-15 07:39:25 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return lights;
|
2017-08-08 06:03:57 +00:00
|
|
|
}
|
|
|
|
|
2022-02-25 07:45:25 +00:00
|
|
|
void CActorLights::ActivateLights() const {
|
2018-12-08 05:30:43 +00:00
|
|
|
if (x298_28_inArea) {
|
|
|
|
if (!x298_26_hasAreaLights || x299_26_ambientOnly) {
|
2022-02-25 07:45:25 +00:00
|
|
|
g_Renderer->SetAmbientColor(zeus::skWhite);
|
|
|
|
CGraphics::DisableAllLights();
|
2018-12-08 05:30:43 +00:00
|
|
|
return;
|
2017-08-08 06:03:57 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2022-02-25 07:45:25 +00:00
|
|
|
auto ambient = x288_ambientColor;
|
|
|
|
ambient.a() = 1.f;
|
|
|
|
g_Renderer->SetAmbientColor(ambient);
|
2017-08-08 06:03:57 +00:00
|
|
|
|
2022-02-25 07:45:25 +00:00
|
|
|
const auto lights = BuildLightVector();
|
|
|
|
if (lights.empty()) {
|
|
|
|
CGraphics::DisableAllLights();
|
|
|
|
} else {
|
2022-03-06 07:46:42 +00:00
|
|
|
for (ERglLight idx = 0; const auto& item : lights) {
|
|
|
|
CGraphics::LoadLight(idx, item);
|
2022-02-25 07:45:25 +00:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
// Sets n LSB to 1
|
2022-03-06 07:46:42 +00:00
|
|
|
CGraphics::SetLightState(static_cast<ERglLight>((1 << lights.size()) + 255));
|
2022-02-25 07:45:25 +00:00
|
|
|
}
|
2017-04-13 19:28:31 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (x298_31_disableWorldLights) {
|
2022-02-25 07:45:25 +00:00
|
|
|
g_Renderer->SetAmbientColor(zeus::skBlack);
|
|
|
|
g_Renderer->SetGXRegister1Color({x2d4_worldLightingLevel});
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CActorLights::DisableAreaLights() {
|
|
|
|
x2b8_maxAreaLights = 0;
|
|
|
|
x298_26_hasAreaLights = false;
|
|
|
|
x298_28_inArea = false;
|
2017-08-10 07:05:27 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
const CLight& CActorLights::GetLight(u32 idx) const {
|
|
|
|
if (x298_28_inArea) {
|
|
|
|
if (idx < x0_areaLights.size())
|
|
|
|
return x0_areaLights[idx];
|
|
|
|
return x144_dynamicLights[idx - x0_areaLights.size()];
|
|
|
|
}
|
|
|
|
return x144_dynamicLights[idx];
|
2016-08-21 00:04:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 CActorLights::GetActiveLightCount() const {
|
|
|
|
if (x298_28_inArea)
|
|
|
|
return x0_areaLights.size() + x144_dynamicLights.size();
|
|
|
|
return x144_dynamicLights.size();
|
2016-08-14 21:11:44 +00:00
|
|
|
}
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|