498 lines
19 KiB
C++
498 lines
19 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
LightSceneInfo.cpp: Light scene info implementation.
|
|
=============================================================================*/
|
|
|
|
#include "LightSceneInfo.h"
|
|
#include "Components/LightComponent.h"
|
|
#include "SceneCore.h"
|
|
#include "ScenePrivate.h"
|
|
#include "DistanceFieldLightingShared.h"
|
|
#include "Misc/LargeWorldRenderPosition.h"
|
|
#include "LocalLightSceneProxy.h"
|
|
#include "ShadowRendering.h"
|
|
|
|
int32 GWholeSceneShadowUnbuiltInteractionThreshold = 500;
|
|
static FAutoConsoleVariableRef CVarWholeSceneShadowUnbuiltInteractionThreshold(
|
|
TEXT("r.Shadow.WholeSceneShadowUnbuiltInteractionThreshold"),
|
|
GWholeSceneShadowUnbuiltInteractionThreshold,
|
|
TEXT("How many unbuilt light-primitive interactions there can be for a light before the light switches to whole scene shadows"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static int32 GRecordInteractionShadowPrimitives = 1;
|
|
FAutoConsoleVariableRef CVarRecordInteractionShadowPrimitives(
|
|
TEXT("r.Shadow.RecordInteractionShadowPrimitives"),
|
|
GRecordInteractionShadowPrimitives,
|
|
TEXT(""),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static int32 GTestMobilityForStaticSceneMembership = 1;
|
|
FAutoConsoleVariableRef CVarTestMobilityForStaticSceneMembership(
|
|
TEXT("r.Light.TestMobilityForStaticSceneMembership"),
|
|
GTestMobilityForStaticSceneMembership,
|
|
TEXT("Deprecated (UE5.5): Temporary flag to switch back to the old behavior (testing the cast static shadow flag)\n.")
|
|
TEXT(" The old behavior checked the HasStaticShadowing flag on the proxy, but that is cleared for VSMs so the new behavior tests the mobility instead."),
|
|
ECVF_RenderThreadSafe | ECVF_ReadOnly);
|
|
|
|
void FLightSceneInfoCompact::Init(FLightSceneInfo* InLightSceneInfo)
|
|
{
|
|
LightSceneInfo = InLightSceneInfo;
|
|
FSphere BoundingSphere = InLightSceneInfo->Proxy->GetBoundingSphere();
|
|
BoundingSphere.W = BoundingSphere.W > 0.0f ? BoundingSphere.W : FLT_MAX;
|
|
FMemory::Memcpy(&BoundingSphereVector,&BoundingSphere,sizeof(BoundingSphereVector));
|
|
Color = InLightSceneInfo->Proxy->GetColor();
|
|
LightType = InLightSceneInfo->Proxy->GetLightType();
|
|
|
|
bCastDynamicShadow = InLightSceneInfo->Proxy->CastsDynamicShadow();
|
|
bCastStaticShadow = InLightSceneInfo->Proxy->CastsStaticShadow();
|
|
bStaticLighting = InLightSceneInfo->Proxy->HasStaticLighting();
|
|
bAffectReflection = InLightSceneInfo->Proxy->AffectReflection();
|
|
bAffectGlobalIllumination = InLightSceneInfo->Proxy->AffectGlobalIllumination();
|
|
bIsMovable = InLightSceneInfo->Proxy->IsMovable();
|
|
CastRaytracedShadow = InLightSceneInfo->Proxy->CastsRaytracedShadow();
|
|
bAllowMegaLights = InLightSceneInfo->Proxy->AllowMegaLights();
|
|
MegaLightsShadowMethod = InLightSceneInfo->Proxy->GetMegaLightsShadowMethod();
|
|
}
|
|
|
|
FLightSceneInfo::FLightSceneInfo(FLightSceneProxy* InProxy, bool InbVisible)
|
|
: bRecordInteractionShadowPrimitives(!!GRecordInteractionShadowPrimitives && InProxy->GetLightType() != ELightComponentType::LightType_Directional)
|
|
, DynamicInteractionOftenMovingPrimitiveList(NULL)
|
|
, DynamicInteractionStaticPrimitiveList(NULL)
|
|
, Proxy(InProxy)
|
|
, Type((ELightComponentType)InProxy->GetLightType())
|
|
, Id(INDEX_NONE)
|
|
, DynamicShadowMapChannel(-1)
|
|
, bPrecomputedLightingIsValid(InProxy->GetLightComponent()->IsPrecomputedLightingValid())
|
|
, bVisible(InbVisible)
|
|
, bEnableLightShaftBloom(InProxy->GetLightComponent()->bEnableLightShaftBloom)
|
|
, BloomScale(InProxy->GetLightComponent()->BloomScale)
|
|
, BloomThreshold(InProxy->GetLightComponent()->BloomThreshold)
|
|
, BloomMaxBrightness(InProxy->GetLightComponent()->BloomMaxBrightness)
|
|
, BloomTint(InProxy->GetLightComponent()->BloomTint)
|
|
, NumUnbuiltInteractions(0)
|
|
, bCreatePerObjectShadowsForDynamicObjects(Proxy->ShouldCreatePerObjectShadowsForDynamicObjects())
|
|
, Scene(InProxy->GetLightComponent()->GetScene()->GetRenderScene())
|
|
{
|
|
// Only visible lights can be added in game
|
|
check(bVisible || GIsEditor);
|
|
|
|
}
|
|
|
|
void FLightSceneInfo::AddToScene()
|
|
{
|
|
const FLightSceneInfoCompact& LightSceneInfoCompact = Scene->Lights[Id];
|
|
|
|
bool bIsValidLightTypeMobile = false;
|
|
if (Scene->GetShadingPath() == EShadingPath::Mobile && ShouldRenderLightViewIndependent())
|
|
{
|
|
const uint8 LightType = Proxy->GetLightType();
|
|
bIsValidLightTypeMobile = LightType == LightType_Rect || LightType == LightType_Point || LightType == LightType_Spot;
|
|
}
|
|
|
|
// Only need to create light interactions for lights that can cast a shadow,
|
|
// As deferred shading doesn't need to know anything about the primitives that a light affects
|
|
if (Proxy->CastsDynamicShadow()
|
|
|| Proxy->CastsStaticShadow()
|
|
// Lights that should be baked need to check for interactions to track unbuilt state correctly
|
|
|| Proxy->HasStaticLighting()
|
|
// Mobile path supports dynamic point/spot lights in the base pass using forward rendering, so we need to know the primitives
|
|
|| bIsValidLightTypeMobile)
|
|
{
|
|
// Directional lights have no finite extent and cannot meaningfully be in the LocalShadowCastingLightOctree
|
|
if (LightSceneInfoCompact.LightType == LightType_Directional)
|
|
{
|
|
//
|
|
Scene->DirectionalShadowCastingLightIDs.Add(Id);
|
|
|
|
// All primitives may interact with a directional light
|
|
for (FPrimitiveSceneInfo *PrimitiveSceneInfo : Scene->Primitives)
|
|
{
|
|
CreateLightPrimitiveInteraction(LightSceneInfoCompact, PrimitiveSceneInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add the light to the scene's light octree.
|
|
Scene->LocalShadowCastingLightOctree.AddElement(LightSceneInfoCompact);
|
|
|
|
Scene->PrimitiveOctree.FindElementsWithBoundsTest(GetBoundingBox(), [&LightSceneInfoCompact, this](const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact)
|
|
{
|
|
CreateLightPrimitiveInteraction(LightSceneInfoCompact, PrimitiveSceneInfoCompact);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FLightSceneInfo::ShouldCreateLightPrimitiveInteraction(const FLightSceneInfoCompact& LightSceneInfoCompact, const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact)
|
|
{
|
|
if (LightSceneInfoCompact.AffectsPrimitive(FBoxSphereBounds(PrimitiveSceneInfoCompact.Bounds), PrimitiveSceneInfoCompact.Proxy))
|
|
{
|
|
// create light interaction and add to light/primitive lists
|
|
return FLightPrimitiveInteraction::ShouldCreate(this, PrimitiveSceneInfoCompact.PrimitiveSceneInfo).bShouldCreate;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FLightSceneInfo::CreateLightPrimitiveInteraction(const FLightSceneInfoCompact& LightSceneInfoCompact, const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact)
|
|
{
|
|
if (LightSceneInfoCompact.AffectsPrimitive(FBoxSphereBounds(PrimitiveSceneInfoCompact.Bounds), PrimitiveSceneInfoCompact.Proxy))
|
|
{
|
|
// create light interaction and add to light/primitive lists
|
|
FLightPrimitiveInteraction::Create(this,PrimitiveSceneInfoCompact.PrimitiveSceneInfo);
|
|
}
|
|
}
|
|
|
|
void FLightSceneInfo::RemoveFromScene()
|
|
{
|
|
if (OctreeId.IsValidId())
|
|
{
|
|
// Remove the light from the octree.
|
|
Scene->LocalShadowCastingLightOctree.RemoveElement(OctreeId);
|
|
OctreeId = FOctreeElementId2();
|
|
}
|
|
else
|
|
{
|
|
Scene->DirectionalShadowCastingLightIDs.RemoveSwap(Id);
|
|
}
|
|
|
|
Scene->CachedShadowMaps.Remove(Id);
|
|
|
|
// Detach the light from the primitives it affects.
|
|
Detach();
|
|
}
|
|
|
|
void FLightSceneInfo::Detach()
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
InteractionShadowPrimitives.Empty();
|
|
|
|
// implicit linked list. The destruction will update this "head" pointer to the next item in the list.
|
|
while(DynamicInteractionOftenMovingPrimitiveList)
|
|
{
|
|
FLightPrimitiveInteraction::Destroy(DynamicInteractionOftenMovingPrimitiveList);
|
|
}
|
|
|
|
while(DynamicInteractionStaticPrimitiveList)
|
|
{
|
|
FLightPrimitiveInteraction::Destroy(DynamicInteractionStaticPrimitiveList);
|
|
}
|
|
}
|
|
|
|
FBoxCenterAndExtent FLightSceneInfo::GetBoundingBox() const
|
|
{
|
|
FSphere BoundingSphere = Proxy->GetBoundingSphere();
|
|
return FBoxCenterAndExtent(BoundingSphere.Center, FVector(BoundingSphere.W, BoundingSphere.W, BoundingSphere.W));
|
|
}
|
|
|
|
inline static bool IsInDesiredSceneSubset(const FViewInfo& View, FLightSceneProxy *Proxy)
|
|
{
|
|
if (!View.bStaticSceneOnly)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (GTestMobilityForStaticSceneMembership)
|
|
{
|
|
return !Proxy->IsMovable();
|
|
}
|
|
else
|
|
{
|
|
return Proxy->HasStaticShadowing();
|
|
}
|
|
}
|
|
|
|
bool FLightSceneInfo::ShouldRenderLight(const FViewInfo& View, bool bOffscreen) const
|
|
{
|
|
// Only render the light if it is in the view frustum
|
|
bool bLocalVisible = bVisible && (bOffscreen ? View.VisibleLightInfos[Id].bInDrawRange : View.VisibleLightInfos[Id].bInViewFrustum);
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
switch(Type)
|
|
{
|
|
case LightType_Directional:
|
|
if(!View.Family->EngineShowFlags.DirectionalLights)
|
|
{
|
|
bLocalVisible = false;
|
|
}
|
|
break;
|
|
case LightType_Point:
|
|
if(!View.Family->EngineShowFlags.PointLights)
|
|
{
|
|
bLocalVisible = false;
|
|
}
|
|
break;
|
|
case LightType_Spot:
|
|
if(!View.Family->EngineShowFlags.SpotLights)
|
|
{
|
|
bLocalVisible = false;
|
|
}
|
|
break;
|
|
case LightType_Rect:
|
|
if(!View.Family->EngineShowFlags.RectLights)
|
|
{
|
|
bLocalVisible = false;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return bLocalVisible
|
|
// Only render lights with static shadowing for reflection captures, since they are only captured at edit time
|
|
&& IsInDesiredSceneSubset(View, Proxy)
|
|
&& (Proxy->GetViewLightingChannelMask() & View.ViewLightingChannelMask)
|
|
// Only render lights in the default channel, or if there are any primitives outside the default channel
|
|
&& (Proxy->GetLightingChannelMask() & GetDefaultLightingChannelMask() || View.bUsesLightingChannels || bOffscreen);
|
|
}
|
|
|
|
bool FLightSceneInfo::ShouldRenderLightViewIndependent() const
|
|
{
|
|
return !Proxy->GetColor().IsAlmostBlack()
|
|
// Only render lights with dynamic lighting or unbuilt static lights
|
|
&& (!Proxy->HasStaticLighting() || !IsPrecomputedLightingValid());
|
|
}
|
|
|
|
bool FLightSceneInfo::ShouldRenderViewIndependentWholeSceneShadows() const
|
|
{
|
|
bool bShouldRenderLight = ShouldRenderLightViewIndependent();
|
|
bool bCastDynamicShadow = Proxy->CastsDynamicShadow();
|
|
|
|
// Also create a whole scene shadow for lights with precomputed shadows that are unbuilt
|
|
const bool bCreateShadowToPreviewStaticLight =
|
|
Proxy->HasStaticShadowing()
|
|
&& bCastDynamicShadow
|
|
&& !IsPrecomputedLightingValid();
|
|
|
|
bool bShouldRenderShadow = bShouldRenderLight && bCastDynamicShadow && (!Proxy->HasStaticLighting() || bCreateShadowToPreviewStaticLight);
|
|
return bShouldRenderShadow;
|
|
}
|
|
|
|
bool FLightSceneInfo::IsPrecomputedLightingValid() const
|
|
{
|
|
return (bPrecomputedLightingIsValid && NumUnbuiltInteractions < GWholeSceneShadowUnbuiltInteractionThreshold) || !Proxy->HasStaticShadowing();
|
|
}
|
|
|
|
void FLightSceneInfo::SetDynamicShadowMapChannel(int32 NewChannel)
|
|
{
|
|
if (Proxy->HasStaticShadowing())
|
|
{
|
|
// This ensure would trigger if several static shadowing light intersects eachother and have the same channel.
|
|
// ensure(Proxy->GetPreviewShadowMapChannel() == NewChannel);
|
|
}
|
|
else
|
|
{
|
|
DynamicShadowMapChannel = NewChannel;
|
|
}
|
|
}
|
|
|
|
int32 FLightSceneInfo::GetDynamicShadowMapChannel() const
|
|
{
|
|
if (Proxy->HasStaticShadowing())
|
|
{
|
|
// Stationary lights get a channel assigned by ReassignStationaryLightChannels
|
|
return Proxy->GetPreviewShadowMapChannel();
|
|
}
|
|
|
|
// Movable lights get a channel assigned when they are added to the scene
|
|
return DynamicShadowMapChannel;
|
|
}
|
|
|
|
const TArray<FLightPrimitiveInteraction*>* FLightSceneInfo::GetInteractionShadowPrimitives() const
|
|
{
|
|
return bRecordInteractionShadowPrimitives ? &InteractionShadowPrimitives : nullptr;
|
|
}
|
|
|
|
FLightPrimitiveInteraction* FLightSceneInfo::GetDynamicInteractionOftenMovingPrimitiveList() const
|
|
{
|
|
return DynamicInteractionOftenMovingPrimitiveList;
|
|
}
|
|
|
|
FLightPrimitiveInteraction* FLightSceneInfo::GetDynamicInteractionStaticPrimitiveList() const
|
|
{
|
|
return DynamicInteractionStaticPrimitiveList;
|
|
}
|
|
|
|
bool FLightSceneInfo::SetupMobileMovableLocalLightShadowParameters(const FViewInfo& View, TConstArrayView<FVisibleLightInfo> VisibleLightInfos, FMobileMovableLocalLightShadowParameters& MobileMovableLocalLightShadowParameters) const
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_FLightSceneProxy_SetupMobileMovableLocalLightShadowParameters);
|
|
|
|
check(Proxy->IsLocalLight());
|
|
const uint8 LightType = Proxy->GetLightType();
|
|
|
|
FLightRenderParameters LightParameters;
|
|
Proxy->GetLightShaderParameters(LightParameters);
|
|
|
|
bool bShouldCastShadow = View.Family->EngineShowFlags.DynamicShadows
|
|
&& GetShadowQuality() > 0
|
|
&& LightType == LightType_Spot
|
|
&& VisibleLightInfos[Id].AllProjectedShadows.Num() > 0
|
|
&& VisibleLightInfos[Id].AllProjectedShadows.Last()->bAllocated;
|
|
|
|
if (bShouldCastShadow)
|
|
{
|
|
FProjectedShadowInfo* ProjectedShadowInfo = VisibleLightInfos[Id].AllProjectedShadows.Last();
|
|
checkSlow(ProjectedShadowInfo && ProjectedShadowInfo->CacheMode != SDCM_StaticPrimitivesOnly);
|
|
|
|
const float TransitionSize = ProjectedShadowInfo->ComputeTransitionSize();
|
|
checkSlow(TransitionSize > 0.0f);
|
|
|
|
float SoftTransitionScale = 1.0f / TransitionSize;
|
|
float ShadowFadeFraction = ProjectedShadowInfo->FadeAlphas[0];
|
|
|
|
MobileMovableLocalLightShadowParameters.SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale = FVector4f(Proxy->GetShadowSharpen() * 7.0f + 1.0f, ShadowFadeFraction, ProjectedShadowInfo->GetShaderReceiverDepthBias(), SoftTransitionScale);
|
|
|
|
const FMatrix WorldToShadowMatrix = ProjectedShadowInfo->GetWorldToShadowMatrix(MobileMovableLocalLightShadowParameters.SpotLightShadowmapMinMax);
|
|
MobileMovableLocalLightShadowParameters.SpotLightShadowWorldToShadowMatrix = FMatrix44f(FTranslationMatrix(LightParameters.WorldPosition) * WorldToShadowMatrix);
|
|
|
|
const FIntPoint ShadowBufferResolution = ProjectedShadowInfo->GetShadowBufferResolution();
|
|
MobileMovableLocalLightShadowParameters.LocalLightShadowBufferSize = FVector4f(ShadowBufferResolution.X, ShadowBufferResolution.Y, 1.0f / ShadowBufferResolution.X, 1.0f / ShadowBufferResolution.Y);
|
|
MobileMovableLocalLightShadowParameters.LocalLightShadowTexture = ProjectedShadowInfo->RenderTargets.DepthTarget->GetRHI();
|
|
MobileMovableLocalLightShadowParameters.LocalLightShadowSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
}
|
|
|
|
return bShouldCastShadow;
|
|
}
|
|
|
|
bool FLightSceneInfo::ShouldRecordShadowSubjectsForMobile() const
|
|
{
|
|
if (Proxy == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// record shadow casters if CSM culling is enabled for the light's mobility type and the culling mode requires the list of casters.
|
|
const bool bCombinedStaticAndCSMEnabled = FReadOnlyCVARCache::MobileEnableStaticAndCSMShadowReceivers();
|
|
const bool bMobileEnableMovableLightCSMShaderCulling = FReadOnlyCVARCache::MobileEnableMovableLightCSMShaderCulling();
|
|
|
|
static auto* CVarMobileCSMShaderCullingMethod = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.Shadow.CSMShaderCullingMethod"));
|
|
const uint32 MobileCSMCullingMode = CVarMobileCSMShaderCullingMethod->GetValueOnAnyThread() & 0xF;
|
|
|
|
const FLightSceneProxy* LightSceneProxy = Proxy;
|
|
|
|
bool bRenderMovableDirectionalLightCSM = LightSceneProxy->IsMovable() && ShouldRenderViewIndependentWholeSceneShadows();
|
|
|
|
bool bLightHasCombinedStaticAndCSMEnabled = bCombinedStaticAndCSMEnabled && LightSceneProxy->UseCSMForDynamicObjects();
|
|
bool bMovableLightUsingCSM = bMobileEnableMovableLightCSMShaderCulling && bRenderMovableDirectionalLightCSM;
|
|
|
|
bool bShouldRecordShadowSubjectsForMobile = (MobileCSMCullingMode == 2 || MobileCSMCullingMode == 3) && (bLightHasCombinedStaticAndCSMEnabled || bMovableLightUsingCSM);
|
|
|
|
return bShouldRecordShadowSubjectsForMobile;
|
|
}
|
|
|
|
uint32 FLightSceneInfo::PackExtraData(bool bAllowStaticLighting, bool bLightFunction, bool bMegaLight, bool bClusteredDeferredSupported) const
|
|
{
|
|
uint32 Result = 0;
|
|
uint32 BitOffset = 0;
|
|
|
|
{
|
|
int32 ShadowMapChannel = Proxy->GetShadowMapChannel();
|
|
int32 CurrentDynamicShadowMapChannel = GetDynamicShadowMapChannel();
|
|
|
|
if (!bAllowStaticLighting)
|
|
{
|
|
ShadowMapChannel = INDEX_NONE;
|
|
}
|
|
|
|
// Static shadowing uses ShadowMapChannel, dynamic shadows are packed into light attenuation using DynamicShadowMapChannel
|
|
uint32 ShadowMapChannelMask =
|
|
(ShadowMapChannel == 0 ? 1 : 0) |
|
|
(ShadowMapChannel == 1 ? 2 : 0) |
|
|
(ShadowMapChannel == 2 ? 4 : 0) |
|
|
(ShadowMapChannel == 3 ? 8 : 0) |
|
|
(CurrentDynamicShadowMapChannel == 0 ? 16 : 0) |
|
|
(CurrentDynamicShadowMapChannel == 1 ? 32 : 0) |
|
|
(CurrentDynamicShadowMapChannel == 2 ? 64 : 0) |
|
|
(CurrentDynamicShadowMapChannel == 3 ? 128 : 0);
|
|
|
|
Result |= ShadowMapChannelMask << BitOffset; BitOffset += 8;
|
|
}
|
|
|
|
Result |= Proxy->GetLightingChannelMask() << BitOffset; BitOffset += 8; // This could be 3 bits
|
|
|
|
// pack light type in this uint32 as well
|
|
Result |= ((uint32)Proxy->GetLightType()) << BitOffset; BitOffset += LightType_NumBits;
|
|
|
|
const uint32 CastShadows = Proxy->CastsDynamicShadow() ? 1 : 0;
|
|
Result |= CastShadows << BitOffset; BitOffset += 1;
|
|
|
|
uint32 HasLightFunction = bLightFunction ? 1 : 0;
|
|
Result |= HasLightFunction << BitOffset; BitOffset += 1;
|
|
|
|
Result |= Proxy->LightFunctionAtlasLightIndex << (BitOffset); BitOffset += 8;
|
|
|
|
uint32 AffectsTranslucentLighting = Proxy->AffectsTranslucentLighting() ? 1 : 0;
|
|
Result |= AffectsTranslucentLighting << (BitOffset); BitOffset += 1;
|
|
|
|
uint32 MegaLight = bMegaLight ? 1 : 0;
|
|
Result |= MegaLight << BitOffset; BitOffset += 1;
|
|
|
|
uint32 ClusteredDeferredSupported = bClusteredDeferredSupported ? 1 : 0;
|
|
Result |= ClusteredDeferredSupported << BitOffset; BitOffset += 1;
|
|
|
|
const uint32 CastVolumetricShadows = Proxy->CastsVolumetricShadow() ? 1 : 0;
|
|
Result |= CastVolumetricShadows << BitOffset;
|
|
BitOffset += 1;
|
|
|
|
// 32 bits used
|
|
|
|
return Result;
|
|
}
|
|
|
|
/** Determines whether two bounding spheres intersect. */
|
|
FORCEINLINE bool AreSpheresNotIntersecting(
|
|
const VectorRegister& A_XYZ,
|
|
const VectorRegister& A_Radius,
|
|
const VectorRegister& B_XYZ,
|
|
const VectorRegister& B_Radius
|
|
)
|
|
{
|
|
const VectorRegister DeltaVector = VectorSubtract(A_XYZ,B_XYZ);
|
|
const VectorRegister DistanceSquared = VectorDot3(DeltaVector,DeltaVector);
|
|
const VectorRegister MaxDistance = VectorAdd(A_Radius,B_Radius);
|
|
const VectorRegister MaxDistanceSquared = VectorMultiply(MaxDistance,MaxDistance);
|
|
return !!VectorAnyGreaterThan(DistanceSquared,MaxDistanceSquared);
|
|
}
|
|
|
|
/**
|
|
* Tests whether this light affects the given primitive. This checks both the primitive and light settings for light relevance
|
|
* and also calls AffectsBounds.
|
|
*
|
|
* @param CompactPrimitiveSceneInfo - The primitive to test.
|
|
* @return True if the light affects the primitive.
|
|
*/
|
|
bool FLightSceneInfoCompact::AffectsPrimitive(const FBoxSphereBounds& PrimitiveBounds, const FPrimitiveSceneProxy* PrimitiveSceneProxy) const
|
|
{
|
|
// Check if the light's bounds intersect the primitive's bounds.
|
|
// Directional lights reach everywhere (the hacky world max radius does not work for large worlds)
|
|
if(LightType != LightType_Directional && AreSpheresNotIntersecting(
|
|
BoundingSphereVector,
|
|
VectorReplicate(BoundingSphereVector,3),
|
|
VectorLoadFloat3(&PrimitiveBounds.Origin),
|
|
VectorLoadFloat1(&PrimitiveBounds.SphereRadius)
|
|
))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Cull based on information in the full scene infos.
|
|
|
|
if(!LightSceneInfo->Proxy->AffectsBounds(PrimitiveBounds))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (LightSceneInfo->Proxy->CastsShadowsFromCinematicObjectsOnly() && !PrimitiveSceneProxy->CastsCinematicShadow())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!(LightSceneInfo->Proxy->GetLightingChannelMask() & PrimitiveSceneProxy->GetLightingChannelMask()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|