You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
2800 lines
95 KiB
C++
2800 lines
95 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
Scene.cpp: Scene manager implementation.
|
|
=============================================================================*/
|
|
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "ShaderCompiler.h"
|
|
#include "StaticMeshResources.h"
|
|
#include "ParameterCollection.h"
|
|
#include "DistanceFieldSurfaceCacheLighting.h"
|
|
#include "EngineModule.h"
|
|
#include "PrecomputedLightVolume.h"
|
|
#include "FXSystem.h"
|
|
#include "DistanceFieldLightingShared.h"
|
|
#include "SpeedTreeWind.h"
|
|
#include "HeightfieldLighting.h"
|
|
|
|
// Enable this define to do slow checks for components being added to the wrong
|
|
// world's scene, when using PIE. This can happen if a PIE component is reattached
|
|
// while GWorld is the editor world, for example.
|
|
#define CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH 0
|
|
|
|
|
|
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FDistanceCullFadeUniformShaderParameters,TEXT("PrimitiveFade"));
|
|
|
|
/** Global primitive uniform buffer resource containing faded in */
|
|
TGlobalResource< FGlobalDistanceCullFadeUniformBuffer > GDistanceCullFadedInUniformBuffer;
|
|
|
|
SIZE_T FStaticMeshDrawListBase::TotalBytesUsed = 0;
|
|
|
|
static FThreadSafeCounter FSceneViewState_UniqueID;
|
|
|
|
/**
|
|
* Holds the info to update SpeedTree wind per unique tree object in the scene, instead of per instance
|
|
*/
|
|
struct FSpeedTreeWindComputation
|
|
{
|
|
explicit FSpeedTreeWindComputation() :
|
|
ReferenceCount(1)
|
|
{
|
|
}
|
|
|
|
/** SpeedTree wind object */
|
|
FSpeedTreeWind Wind;
|
|
|
|
/** Uniform buffer shared between trees of the same type. */
|
|
TUniformBuffer<FSpeedTreeUniformParameters> UniformBuffer;
|
|
|
|
int32 ReferenceCount;
|
|
};
|
|
|
|
|
|
/** Default constructor. */
|
|
FSceneViewState::FSceneViewState()
|
|
: OcclusionQueryPool(RQT_Occlusion)
|
|
{
|
|
UniqueID = FSceneViewState_UniqueID.Increment();
|
|
OcclusionFrameCounter = 0;
|
|
LastRenderTime = -FLT_MAX;
|
|
LastRenderTimeDelta = 0.0f;
|
|
MotionBlurTimeScale = 1.0f;
|
|
PrevViewMatrixForOcclusionQuery.SetIdentity();
|
|
PrevViewOriginForOcclusionQuery = FVector::ZeroVector;
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
bIsFreezing = false;
|
|
bIsFrozen = false;
|
|
#endif
|
|
// Register this object as a resource, so it will receive device reset notifications.
|
|
if ( IsInGameThread() )
|
|
{
|
|
BeginInitResource(this);
|
|
}
|
|
else
|
|
{
|
|
InitResource();
|
|
}
|
|
CachedVisibilityChunk = NULL;
|
|
CachedVisibilityHandlerId = INDEX_NONE;
|
|
CachedVisibilityBucketIndex = INDEX_NONE;
|
|
CachedVisibilityChunkIndex = INDEX_NONE;
|
|
MIDUsedCount = 0;
|
|
TemporalAASampleIndex = 0;
|
|
TemporalAASampleCount = 1;
|
|
AOTileIntersectionResources = NULL;
|
|
bBokehDOFHistory = true;
|
|
bBokehDOFHistory2 = true;
|
|
|
|
LightPropagationVolume = NULL;
|
|
HeightfieldLightingAtlas = NULL;
|
|
|
|
for (int32 CascadeIndex = 0; CascadeIndex < ARRAY_COUNT(TranslucencyLightingCacheAllocations); CascadeIndex++)
|
|
{
|
|
TranslucencyLightingCacheAllocations[CascadeIndex] = NULL;
|
|
}
|
|
|
|
#if BUFFERED_OCCLUSION_QUERIES
|
|
NumBufferedFrames = FOcclusionQueryHelpers::GetNumBufferedFrames();
|
|
ShadowOcclusionQueryMaps.Empty(NumBufferedFrames);
|
|
ShadowOcclusionQueryMaps.AddZeroed(NumBufferedFrames);
|
|
#endif
|
|
}
|
|
|
|
void DestroyRenderResource(FRenderResource* RenderResource)
|
|
{
|
|
if (RenderResource)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
DestroySceneViewStateRenderResource,
|
|
FRenderResource*, RenderResourceRT, RenderResource,
|
|
{
|
|
RenderResourceRT->ReleaseResource();
|
|
delete RenderResourceRT;
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
FSceneViewState::~FSceneViewState()
|
|
{
|
|
CachedVisibilityChunk = NULL;
|
|
|
|
for (int32 CascadeIndex = 0; CascadeIndex < ARRAY_COUNT(TranslucencyLightingCacheAllocations); CascadeIndex++)
|
|
{
|
|
delete TranslucencyLightingCacheAllocations[CascadeIndex];
|
|
}
|
|
|
|
DestroyRenderResource(HeightfieldLightingAtlas);
|
|
DestroyRenderResource(AOTileIntersectionResources);
|
|
AOTileIntersectionResources = NULL;
|
|
DestroyLightPropagationVolume();
|
|
}
|
|
|
|
FDistanceFieldSceneData::FDistanceFieldSceneData(EShaderPlatform ShaderPlatform)
|
|
: NumObjectsInBuffer(0)
|
|
, ObjectBuffers(NULL)
|
|
, SurfelBuffers(NULL)
|
|
, InstancedSurfelBuffers(NULL)
|
|
, AtlasGeneration(0)
|
|
{
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GenerateMeshDistanceFields"));
|
|
|
|
bTrackPrimitives = (DoesPlatformSupportDistanceFieldAO(ShaderPlatform) || DoesPlatformSupportDistanceFieldShadowing(ShaderPlatform)) && CVar->GetValueOnGameThread() != 0;
|
|
}
|
|
|
|
FDistanceFieldSceneData::~FDistanceFieldSceneData()
|
|
{
|
|
delete ObjectBuffers;
|
|
}
|
|
|
|
void FDistanceFieldSceneData::AddPrimitive(FPrimitiveSceneInfo* InPrimitive)
|
|
{
|
|
const FPrimitiveSceneProxy* Proxy = InPrimitive->Proxy;
|
|
|
|
if (bTrackPrimitives
|
|
&& Proxy->CastsDynamicShadow()
|
|
&& Proxy->AffectsDistanceFieldLighting())
|
|
{
|
|
if (Proxy->SupportsHeightfieldRepresentation())
|
|
{
|
|
HeightfieldPrimitives.Add(InPrimitive);
|
|
}
|
|
|
|
if (Proxy->SupportsDistanceFieldRepresentation())
|
|
{
|
|
checkSlow(!PendingAddOperations.Contains(InPrimitive));
|
|
checkSlow(!PendingUpdateOperations.Contains(InPrimitive));
|
|
PendingAddOperations.Add(InPrimitive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDistanceFieldSceneData::UpdatePrimitive(FPrimitiveSceneInfo* InPrimitive)
|
|
{
|
|
const FPrimitiveSceneProxy* Proxy = InPrimitive->Proxy;
|
|
|
|
if (bTrackPrimitives
|
|
&& Proxy->CastsDynamicShadow()
|
|
&& Proxy->AffectsDistanceFieldLighting()
|
|
&& Proxy->SupportsDistanceFieldRepresentation()
|
|
&& !PendingAddOperations.Contains(InPrimitive)
|
|
// This is needed to prevent infinite buildup when DF features are off such that the pending operations don't get consumed
|
|
&& !PendingUpdateOperations.Contains(InPrimitive)
|
|
// This can happen when the primitive fails to allocate from the SDF atlas
|
|
&& InPrimitive->DistanceFieldInstanceIndices.Num() > 0)
|
|
{
|
|
PendingUpdateOperations.Add(InPrimitive);
|
|
}
|
|
}
|
|
|
|
void FDistanceFieldSceneData::RemovePrimitive(FPrimitiveSceneInfo* InPrimitive)
|
|
{
|
|
const FPrimitiveSceneProxy* Proxy = InPrimitive->Proxy;
|
|
|
|
if (bTrackPrimitives && Proxy->AffectsDistanceFieldLighting())
|
|
{
|
|
if (Proxy->SupportsDistanceFieldRepresentation())
|
|
{
|
|
PendingAddOperations.Remove(InPrimitive);
|
|
PendingUpdateOperations.Remove(InPrimitive);
|
|
|
|
if (InPrimitive->DistanceFieldInstanceIndices.Num() > 0)
|
|
{
|
|
PendingRemoveOperations.Add(FPrimitiveRemoveInfo(InPrimitive));
|
|
}
|
|
|
|
InPrimitive->DistanceFieldInstanceIndices.Empty();
|
|
}
|
|
|
|
if (Proxy->SupportsHeightfieldRepresentation())
|
|
{
|
|
HeightfieldPrimitives.Remove(InPrimitive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDistanceFieldSceneData::Release()
|
|
{
|
|
if (ObjectBuffers)
|
|
{
|
|
ObjectBuffers->Release();
|
|
}
|
|
}
|
|
|
|
void FDistanceFieldSceneData::VerifyIntegrity()
|
|
{
|
|
check(NumObjectsInBuffer == PrimitiveInstanceMapping.Num());
|
|
|
|
for (int32 PrimitiveInstanceIndex = 0; PrimitiveInstanceIndex < PrimitiveInstanceMapping.Num(); PrimitiveInstanceIndex++)
|
|
{
|
|
const FPrimitiveAndInstance& PrimitiveAndInstance = PrimitiveInstanceMapping[PrimitiveInstanceIndex];
|
|
|
|
check(PrimitiveAndInstance.Primitive && PrimitiveAndInstance.Primitive->DistanceFieldInstanceIndices.Num() > 0);
|
|
check(PrimitiveAndInstance.Primitive->DistanceFieldInstanceIndices.IsValidIndex(PrimitiveAndInstance.InstanceIndex));
|
|
|
|
const int32 InstanceIndex = PrimitiveAndInstance.Primitive->DistanceFieldInstanceIndices[PrimitiveAndInstance.InstanceIndex];
|
|
check(InstanceIndex == PrimitiveInstanceIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the FX system associated with the scene.
|
|
*/
|
|
void FScene::SetFXSystem( class FFXSystemInterface* InFXSystem )
|
|
{
|
|
FXSystem = InFXSystem;
|
|
}
|
|
|
|
/**
|
|
* Get the FX system associated with the scene.
|
|
*/
|
|
FFXSystemInterface* FScene::GetFXSystem()
|
|
{
|
|
return FXSystem;
|
|
}
|
|
|
|
void FScene::SetClearMotionBlurInfoGameThread()
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
ShouldClearMBInfoCommand,
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->MotionBlurInfoData.SetClearMotionBlurInfo();
|
|
});
|
|
}
|
|
|
|
void FScene::UpdateParameterCollections(const TArray<FMaterialParameterCollectionInstanceResource*>& InParameterCollections)
|
|
{
|
|
// Empy the scene's map so any unused uniform buffers will be released
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
ClearParameterCollectionsCommand,
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->ParameterCollections.Empty();
|
|
});
|
|
|
|
// Add each existing parameter collection id and its uniform buffer
|
|
for (int32 CollectionIndex = 0; CollectionIndex < InParameterCollections.Num(); CollectionIndex++)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
AddParameterCollectionCommand,
|
|
FScene*,Scene,this,
|
|
FMaterialParameterCollectionInstanceResource*,InstanceResource,InParameterCollections[CollectionIndex],
|
|
{
|
|
Scene->ParameterCollections.Add(InstanceResource->GetId(), InstanceResource->GetUniformBuffer());
|
|
});
|
|
}
|
|
}
|
|
|
|
SIZE_T FScene::GetSizeBytes() const
|
|
{
|
|
return sizeof(*this)
|
|
+ Primitives.GetAllocatedSize()
|
|
+ Lights.GetAllocatedSize()
|
|
+ StaticMeshes.GetAllocatedSize()
|
|
+ ExponentialFogs.GetAllocatedSize()
|
|
+ WindSources.GetAllocatedSize()
|
|
+ SpeedTreeVertexFactoryMap.GetAllocatedSize()
|
|
+ SpeedTreeWindComputationMap.GetAllocatedSize()
|
|
+ LightOctree.GetSizeBytes()
|
|
+ PrimitiveOctree.GetSizeBytes();
|
|
}
|
|
|
|
void FScene::CheckPrimitiveArrays()
|
|
{
|
|
check(Primitives.Num() == PrimitiveBounds.Num());
|
|
check(Primitives.Num() == PrimitiveVisibilityIds.Num());
|
|
check(Primitives.Num() == PrimitiveOcclusionFlags.Num());
|
|
check(Primitives.Num() == PrimitiveComponentIds.Num());
|
|
check(Primitives.Num() == PrimitiveOcclusionBounds.Num());
|
|
}
|
|
|
|
void FScene::AddPrimitiveSceneInfo_RenderThread(FRHICommandListImmediate& RHICmdList, FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_AddScenePrimitiveRenderThreadTime);
|
|
|
|
CheckPrimitiveArrays();
|
|
|
|
int32 PrimitiveIndex = Primitives.Add(PrimitiveSceneInfo);
|
|
PrimitiveSceneInfo->PackedIndex = PrimitiveIndex;
|
|
|
|
PrimitiveBounds.AddUninitialized();
|
|
PrimitiveVisibilityIds.AddUninitialized();
|
|
PrimitiveOcclusionFlags.AddUninitialized();
|
|
PrimitiveComponentIds.AddUninitialized();
|
|
PrimitiveOcclusionBounds.AddUninitialized();
|
|
|
|
CheckPrimitiveArrays();
|
|
|
|
// Add the primitive to its shadow parent's linked list of children.
|
|
// Note: must happen before AddToScene because AddToScene depends on LightingAttachmentRoot
|
|
PrimitiveSceneInfo->LinkAttachmentGroup();
|
|
|
|
// Set lod Parent information if valid
|
|
PrimitiveSceneInfo->LinkLODParentComponent();
|
|
|
|
// Add the primitive to the scene.
|
|
PrimitiveSceneInfo->AddToScene(RHICmdList, true);
|
|
|
|
DistanceFieldSceneData.AddPrimitive(PrimitiveSceneInfo);
|
|
|
|
// LOD Parent, if this is LOD parent, we should update Proxy Scene Info
|
|
// LOD parent gets removed WHEN no children is accessing
|
|
// LOD parent can be recreated as scene updates
|
|
// I update if the parent component ID is still valid
|
|
// @Todo : really remove it if you know this is being destroyed - should happen from game thread as streaming in/out
|
|
SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, PrimitiveSceneInfo);
|
|
}
|
|
|
|
/**
|
|
* Verifies that a component is added to the proper scene
|
|
*
|
|
* @param Component Component to verify
|
|
* @param World World who's scene the primitive is being attached to
|
|
*/
|
|
FORCEINLINE static void VerifyProperPIEScene(UPrimitiveComponent* Component, UWorld* World)
|
|
{
|
|
#if CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH
|
|
checkf(Component->GetOuter() == GetTransientPackage() ||
|
|
(FPackageName::GetLongPackageAssetName(Component->GetOutermost()->GetName()).StartsWith(PLAYWORLD_PACKAGE_PREFIX) ==
|
|
FPackageName::GetLongPackageAssetName(World->GetOutermost()->GetName()).StartsWith(PLAYWORLD_PACKAGE_PREFIX)),
|
|
TEXT("The component %s was added to the wrong world's scene (due to PIE). The callstack should tell you why"),
|
|
*Component->GetFullName()
|
|
);
|
|
#endif
|
|
}
|
|
|
|
FScene::FScene(UWorld* InWorld, bool bInRequiresHitProxies, bool bInIsEditorScene, bool bCreateFXSystem, ERHIFeatureLevel::Type InFeatureLevel)
|
|
: World(InWorld)
|
|
, FXSystem(NULL)
|
|
, bStaticDrawListsMobileHDR(false)
|
|
, bStaticDrawListsMobileHDR32bpp(false)
|
|
, StaticDrawListsEarlyZPassMode(0)
|
|
, bScenesPrimitivesNeedStaticMeshElementUpdate(false)
|
|
, SkyLight(NULL)
|
|
, SimpleDirectionalLight(NULL)
|
|
, SunLight(NULL)
|
|
, ReflectionSceneData(InFeatureLevel)
|
|
, IndirectLightingCache(InFeatureLevel)
|
|
, SurfaceCacheResources(NULL)
|
|
, DistanceFieldSceneData(GShaderPlatformForFeatureLevel[InFeatureLevel])
|
|
, PreshadowCacheLayout(0, 0, 0, 0, false, false)
|
|
, AtmosphericFog(NULL)
|
|
, PrecomputedVisibilityHandler(NULL)
|
|
, LightOctree(FVector::ZeroVector,HALF_WORLD_MAX)
|
|
, PrimitiveOctree(FVector::ZeroVector,HALF_WORLD_MAX)
|
|
, bRequiresHitProxies(bInRequiresHitProxies)
|
|
, bIsEditorScene(bInIsEditorScene)
|
|
, NumUncachedStaticLightingInteractions(0)
|
|
, UpperDynamicSkylightColor(FLinearColor::Black)
|
|
, LowerDynamicSkylightColor(FLinearColor::Black)
|
|
, SceneLODHierarchy(this)
|
|
, NumVisibleLights_GameThread(0)
|
|
, NumEnabledSkylights_GameThread(0)
|
|
{
|
|
check(World);
|
|
World->Scene = this;
|
|
|
|
FeatureLevel = World->FeatureLevel;
|
|
|
|
static auto* MobileHDRCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileHDR"));
|
|
static auto* MobileHDR32bppCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileHDR32bpp"));
|
|
bStaticDrawListsMobileHDR = MobileHDRCvar->GetValueOnAnyThread() == 1;
|
|
bStaticDrawListsMobileHDR32bpp = bStaticDrawListsMobileHDR && (GSupportsRenderTargetFormat_PF_FloatRGBA == false || MobileHDR32bppCvar->GetValueOnAnyThread() == 1);
|
|
|
|
static auto* EarlyZPassCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.EarlyZPass"));
|
|
StaticDrawListsEarlyZPassMode = EarlyZPassCvar->GetValueOnAnyThread();
|
|
|
|
if (World->FXSystem)
|
|
{
|
|
FFXSystemInterface::Destroy(World->FXSystem);
|
|
}
|
|
|
|
if (bCreateFXSystem)
|
|
{
|
|
World->CreateFXSystem();
|
|
}
|
|
else
|
|
{
|
|
World->FXSystem = NULL;
|
|
SetFXSystem(NULL);
|
|
}
|
|
|
|
World->UpdateParameterCollectionInstances(false);
|
|
}
|
|
|
|
FScene::~FScene()
|
|
{
|
|
#if 0 // if you have component that has invalid scene, try this code to see this is reason.
|
|
for (FObjectIterator Iter(UActorComponent::StaticClass()); Iter; ++Iter)
|
|
{
|
|
UActorComponent * ActorComp = CastChecked<UActorComponent>(*Iter);
|
|
if (ActorComp->GetScene() == this)
|
|
{
|
|
UE_LOG(LogRenderer, Log, TEXT("%s's scene is going to get invalidated"), *ActorComp->GetName());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ReflectionSceneData.CubemapArray.ReleaseResource();
|
|
IndirectLightingCache.ReleaseResource();
|
|
DistanceFieldSceneData.Release();
|
|
|
|
if (SurfaceCacheResources)
|
|
{
|
|
SurfaceCacheResources->ReleaseResource();
|
|
delete SurfaceCacheResources;
|
|
SurfaceCacheResources = NULL;
|
|
}
|
|
|
|
if (AtmosphericFog)
|
|
{
|
|
delete AtmosphericFog;
|
|
AtmosphericFog = NULL;
|
|
}
|
|
}
|
|
|
|
void FScene::AddPrimitive(UPrimitiveComponent* Primitive)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_AddScenePrimitiveGT);
|
|
|
|
checkf(!Primitive->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Primitive->GetFullName());
|
|
|
|
|
|
// Save the world transform for next time the primitive is added to the scene
|
|
float DeltaTime = GetWorld()->GetTimeSeconds() - Primitive->LastSubmitTime;
|
|
if ( DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f )
|
|
{
|
|
// Time was reset?
|
|
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
|
|
}
|
|
else if ( DeltaTime > 0.0001f )
|
|
{
|
|
// First call for the new frame?
|
|
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
|
|
}
|
|
|
|
// Create the primitive's scene proxy.
|
|
FPrimitiveSceneProxy* PrimitiveSceneProxy = Primitive->CreateSceneProxy();
|
|
Primitive->SceneProxy = PrimitiveSceneProxy;
|
|
if(!PrimitiveSceneProxy)
|
|
{
|
|
// Primitives which don't have a proxy are irrelevant to the scene manager.
|
|
return;
|
|
}
|
|
|
|
// Cache the primitive's initial transform.
|
|
FMatrix RenderMatrix = Primitive->GetRenderMatrix();
|
|
FVector OwnerPosition(0);
|
|
|
|
AActor* Owner = Primitive->GetOwner();
|
|
if (Owner)
|
|
{
|
|
OwnerPosition = Owner->GetActorLocation();
|
|
}
|
|
|
|
struct FCreateRenderThreadParameters
|
|
{
|
|
FPrimitiveSceneProxy* PrimitiveSceneProxy;
|
|
FMatrix RenderMatrix;
|
|
FBoxSphereBounds WorldBounds;
|
|
FVector OwnerPosition;
|
|
FBoxSphereBounds LocalBounds;
|
|
};
|
|
FCreateRenderThreadParameters Params =
|
|
{
|
|
PrimitiveSceneProxy,
|
|
RenderMatrix,
|
|
Primitive->Bounds,
|
|
OwnerPosition,
|
|
Primitive->CalcBounds(FTransform::Identity)
|
|
};
|
|
// Create any RenderThreadResources required.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
FCreateRenderThreadResourcesCommand,
|
|
FCreateRenderThreadParameters, Params, Params,
|
|
{
|
|
FPrimitiveSceneProxy* SceneProxy = Params.PrimitiveSceneProxy;
|
|
FScopeCycleCounter Context(SceneProxy->GetStatId());
|
|
SceneProxy->SetTransform(Params.RenderMatrix, Params.WorldBounds, Params.LocalBounds, Params.OwnerPosition);
|
|
|
|
// Create any RenderThreadResources required.
|
|
SceneProxy->CreateRenderThreadResources();
|
|
});
|
|
|
|
// Create the primitive scene info.
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = new FPrimitiveSceneInfo(Primitive,this);
|
|
PrimitiveSceneProxy->PrimitiveSceneInfo = PrimitiveSceneInfo;
|
|
|
|
INC_DWORD_STAT_BY( STAT_GameToRendererMallocTotal, PrimitiveSceneProxy->GetMemoryFootprint() + PrimitiveSceneInfo->GetMemoryFootprint() );
|
|
|
|
// Verify the primitive is valid (this will compile away to a nop without CHECK_FOR_PIE_PRIMITIVE_ATTACH_SCENE_MISMATCH)
|
|
VerifyProperPIEScene(Primitive, World);
|
|
|
|
// Increment the attachment counter, the primitive is about to be attached to the scene.
|
|
Primitive->AttachmentCounter.Increment();
|
|
|
|
// Send a command to the rendering thread to add the primitive to the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddPrimitiveCommand,
|
|
FScene*,Scene,this,
|
|
FPrimitiveSceneInfo*,PrimitiveSceneInfo,PrimitiveSceneInfo,
|
|
{
|
|
FScopeCycleCounter Context(PrimitiveSceneInfo->Proxy->GetStatId());
|
|
Scene->AddPrimitiveSceneInfo_RenderThread(RHICmdList, PrimitiveSceneInfo);
|
|
});
|
|
|
|
}
|
|
|
|
void FScene::UpdatePrimitiveTransform_RenderThread(FRHICommandListImmediate& RHICmdList, FPrimitiveSceneProxy* PrimitiveSceneProxy, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, const FMatrix& LocalToWorld, const FVector& OwnerPosition)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_UpdatePrimitiveTransformRenderThreadTime);
|
|
|
|
const bool bUpdateStaticDrawLists = !PrimitiveSceneProxy->StaticElementsAlwaysUseProxyPrimitiveUniformBuffer();
|
|
|
|
// Remove the primitive from the scene at its old location
|
|
// (note that the octree update relies on the bounds not being modified yet).
|
|
PrimitiveSceneProxy->GetPrimitiveSceneInfo()->RemoveFromScene(bUpdateStaticDrawLists);
|
|
|
|
// Update the primitive motion blur information.
|
|
// hack
|
|
FScene* Scene = (FScene*)&PrimitiveSceneProxy->GetScene();
|
|
|
|
Scene->MotionBlurInfoData.UpdatePrimitiveMotionBlur(PrimitiveSceneProxy->GetPrimitiveSceneInfo());
|
|
|
|
// Update the primitive transform.
|
|
PrimitiveSceneProxy->SetTransform(LocalToWorld, WorldBounds, LocalBounds, OwnerPosition);
|
|
|
|
DistanceFieldSceneData.UpdatePrimitive(PrimitiveSceneProxy->GetPrimitiveSceneInfo());
|
|
|
|
// If the primitive has static mesh elements, it should have returned true from ShouldRecreateProxyOnUpdateTransform!
|
|
check(!(bUpdateStaticDrawLists && PrimitiveSceneProxy->GetPrimitiveSceneInfo()->StaticMeshes.Num()));
|
|
|
|
// Re-add the primitive to the scene with the new transform.
|
|
PrimitiveSceneProxy->GetPrimitiveSceneInfo()->AddToScene(RHICmdList, bUpdateStaticDrawLists);
|
|
}
|
|
|
|
void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_UpdatePrimitiveTransformGT);
|
|
|
|
// Save the world transform for next time the primitive is added to the scene
|
|
float DeltaTime = GetWorld()->GetTimeSeconds() - Primitive->LastSubmitTime;
|
|
if ( DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f )
|
|
{
|
|
// Time was reset?
|
|
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
|
|
}
|
|
else if ( DeltaTime > 0.0001f )
|
|
{
|
|
// First call for the new frame?
|
|
Primitive->LastSubmitTime = GetWorld()->GetTimeSeconds();
|
|
}
|
|
|
|
AActor* Owner = Primitive->GetOwner();
|
|
|
|
// If the root component of an actor is being moved, update all the actor position of the other components sharing that actor
|
|
if (Owner && Owner->GetRootComponent() == Primitive)
|
|
{
|
|
TInlineComponentArray<UPrimitiveComponent*> Components;
|
|
Owner->GetComponents(Components);
|
|
for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
|
|
{
|
|
UPrimitiveComponent* PrimitiveComponent = Components[ComponentIndex];
|
|
|
|
// Only update components that are already attached
|
|
if (PrimitiveComponent
|
|
&& PrimitiveComponent->SceneProxy
|
|
&& PrimitiveComponent != Primitive
|
|
// Don't bother if it is going to have its transform updated anyway
|
|
&& !PrimitiveComponent->IsRenderTransformDirty()
|
|
&& !PrimitiveComponent->IsRenderStateDirty())
|
|
{
|
|
PrimitiveComponent->SceneProxy->UpdateActorPosition(Owner->GetActorLocation());
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Primitive->SceneProxy)
|
|
{
|
|
// Check if the primitive needs to recreate its proxy for the transform update.
|
|
if(Primitive->ShouldRecreateProxyOnUpdateTransform())
|
|
{
|
|
// Re-add the primitive from scratch to recreate the primitive's proxy.
|
|
RemovePrimitive(Primitive);
|
|
AddPrimitive(Primitive);
|
|
}
|
|
else
|
|
{
|
|
FVector OwnerPosition(0);
|
|
|
|
AActor* Actor = Primitive->GetOwner();
|
|
if (Actor != NULL)
|
|
{
|
|
OwnerPosition = Actor->GetActorLocation();
|
|
}
|
|
|
|
struct FPrimitiveUpdateParams
|
|
{
|
|
FScene* Scene;
|
|
FPrimitiveSceneProxy* PrimitiveSceneProxy;
|
|
FBoxSphereBounds WorldBounds;
|
|
FBoxSphereBounds LocalBounds;
|
|
FMatrix LocalToWorld;
|
|
FVector OwnerPosition;
|
|
};
|
|
|
|
FPrimitiveUpdateParams UpdateParams;
|
|
UpdateParams.Scene = this;
|
|
UpdateParams.PrimitiveSceneProxy = Primitive->SceneProxy;
|
|
UpdateParams.WorldBounds = Primitive->Bounds;
|
|
UpdateParams.LocalToWorld = Primitive->GetRenderMatrix();
|
|
UpdateParams.OwnerPosition = OwnerPosition;
|
|
UpdateParams.LocalBounds = Primitive->CalcBounds(FTransform::Identity);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
UpdateTransformCommand,
|
|
FPrimitiveUpdateParams,UpdateParams,UpdateParams,
|
|
{
|
|
FScopeCycleCounter Context(UpdateParams.PrimitiveSceneProxy->GetStatId());
|
|
UpdateParams.Scene->UpdatePrimitiveTransform_RenderThread(RHICmdList, UpdateParams.PrimitiveSceneProxy, UpdateParams.WorldBounds, UpdateParams.LocalBounds, UpdateParams.LocalToWorld, UpdateParams.OwnerPosition);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the primitive doesn't have a scene info object yet, it must be added from scratch.
|
|
AddPrimitive(Primitive);
|
|
}
|
|
}
|
|
|
|
void FScene::UpdatePrimitiveLightingAttachmentRoot(UPrimitiveComponent* Primitive)
|
|
{
|
|
const UPrimitiveComponent* NewLightingAttachmentRoot = Cast<UPrimitiveComponent>(Primitive->GetAttachmentRoot());
|
|
|
|
if (NewLightingAttachmentRoot == Primitive)
|
|
{
|
|
NewLightingAttachmentRoot = NULL;
|
|
}
|
|
|
|
FPrimitiveComponentId NewComponentId = NewLightingAttachmentRoot ? NewLightingAttachmentRoot->ComponentId : FPrimitiveComponentId();
|
|
|
|
if (Primitive->SceneProxy)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
UpdatePrimitiveAttachment,
|
|
FPrimitiveSceneProxy*,Proxy,Primitive->SceneProxy,
|
|
FPrimitiveComponentId,NewComponentId,NewComponentId,
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveInfo = Proxy->GetPrimitiveSceneInfo();
|
|
PrimitiveInfo->UnlinkAttachmentGroup();
|
|
PrimitiveInfo->LightingAttachmentRoot = NewComponentId;
|
|
PrimitiveInfo->LinkAttachmentGroup();
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::UpdatePrimitiveAttachment(UPrimitiveComponent* Primitive)
|
|
{
|
|
TArray<USceneComponent*, TInlineAllocator<1> > ProcessStack;
|
|
ProcessStack.Push(Primitive);
|
|
|
|
// Walk down the tree updating, because the scene's attachment data structures must be updated if the root of the attachment tree changes
|
|
while (ProcessStack.Num() > 0)
|
|
{
|
|
USceneComponent* Current = ProcessStack.Pop();
|
|
UPrimitiveComponent* CurrentPrimitive = Cast<UPrimitiveComponent>(Current);
|
|
check(Current);
|
|
|
|
if (CurrentPrimitive
|
|
&& CurrentPrimitive->GetWorld()
|
|
&& CurrentPrimitive->GetWorld()->Scene
|
|
&& CurrentPrimitive->GetWorld()->Scene == this
|
|
&& CurrentPrimitive->ShouldComponentAddToScene())
|
|
{
|
|
UpdatePrimitiveLightingAttachmentRoot(CurrentPrimitive);
|
|
}
|
|
|
|
for (int32 ChildIndex = 0; ChildIndex < Current->AttachChildren.Num(); ChildIndex++)
|
|
{
|
|
USceneComponent* ChildComponent = Current->AttachChildren[ChildIndex];
|
|
if (ChildComponent)
|
|
{
|
|
ProcessStack.Push(ChildComponent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FScene::RemovePrimitiveSceneInfo_RenderThread(FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_RemoveScenePrimitiveTime);
|
|
|
|
// clear it up, parent is getting removed
|
|
SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, nullptr);
|
|
|
|
CheckPrimitiveArrays();
|
|
|
|
int32 PrimitiveIndex = PrimitiveSceneInfo->PackedIndex;
|
|
Primitives.RemoveAtSwap(PrimitiveIndex);
|
|
PrimitiveBounds.RemoveAtSwap(PrimitiveIndex);
|
|
PrimitiveVisibilityIds.RemoveAtSwap(PrimitiveIndex);
|
|
PrimitiveOcclusionFlags.RemoveAtSwap(PrimitiveIndex);
|
|
PrimitiveComponentIds.RemoveAtSwap(PrimitiveIndex);
|
|
PrimitiveOcclusionBounds.RemoveAtSwap(PrimitiveIndex);
|
|
if (Primitives.IsValidIndex(PrimitiveIndex))
|
|
{
|
|
FPrimitiveSceneInfo* OtherPrimitive = Primitives[PrimitiveIndex];
|
|
OtherPrimitive->PackedIndex = PrimitiveIndex;
|
|
}
|
|
|
|
CheckPrimitiveArrays();
|
|
|
|
// Update the primitive's motion blur information.
|
|
MotionBlurInfoData.RemovePrimitiveMotionBlur(PrimitiveSceneInfo);
|
|
|
|
// Unlink the primitive from its shadow parent.
|
|
PrimitiveSceneInfo->UnlinkAttachmentGroup();
|
|
|
|
// Unlink the LOD parent info if valid
|
|
PrimitiveSceneInfo->UnlinkLODParentComponent();
|
|
|
|
// Remove the primitive from the scene.
|
|
PrimitiveSceneInfo->RemoveFromScene(true);
|
|
|
|
DistanceFieldSceneData.RemovePrimitive(PrimitiveSceneInfo);
|
|
|
|
// free the primitive scene proxy.
|
|
delete PrimitiveSceneInfo->Proxy;
|
|
}
|
|
|
|
void FScene::RemovePrimitive( UPrimitiveComponent* Primitive )
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_RemoveScenePrimitiveGT);
|
|
|
|
FPrimitiveSceneProxy* PrimitiveSceneProxy = Primitive->SceneProxy;
|
|
|
|
if(PrimitiveSceneProxy)
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
|
|
|
|
// Disassociate the primitive's scene proxy.
|
|
Primitive->SceneProxy = NULL;
|
|
|
|
// Send a command to the rendering thread to remove the primitive from the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FRemovePrimitiveCommand,
|
|
FScene*,Scene,this,
|
|
FPrimitiveSceneInfo*,PrimitiveSceneInfo,PrimitiveSceneProxy->GetPrimitiveSceneInfo(),
|
|
FThreadSafeCounter*,AttachmentCounter,&Primitive->AttachmentCounter,
|
|
{
|
|
FScopeCycleCounter Context(PrimitiveSceneInfo->Proxy->GetStatId());
|
|
Scene->RemovePrimitiveSceneInfo_RenderThread(PrimitiveSceneInfo);
|
|
AttachmentCounter->Decrement();
|
|
});
|
|
|
|
// Delete the PrimitiveSceneInfo on the game thread after the rendering thread has processed its removal.
|
|
// This must be done on the game thread because the hit proxy references (and possibly other members) need to be freed on the game thread.
|
|
BeginCleanup(PrimitiveSceneInfo);
|
|
}
|
|
}
|
|
|
|
void FScene::ReleasePrimitive( UPrimitiveComponent* PrimitiveComponent )
|
|
{
|
|
// Send a command to the rendering thread to clean up any state dependent on this primitive
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FReleasePrimitiveCommand,
|
|
FScene*,Scene,this,
|
|
FPrimitiveComponentId,PrimitiveComponentId,PrimitiveComponent->ComponentId,
|
|
{
|
|
// Free the space in the indirect lighting cache
|
|
Scene->IndirectLightingCache.ReleasePrimitive(PrimitiveComponentId);
|
|
});
|
|
}
|
|
|
|
void FScene::AddLightSceneInfo_RenderThread(FLightSceneInfo* LightSceneInfo)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_AddSceneLightTime);
|
|
|
|
check(LightSceneInfo->bVisible);
|
|
|
|
// Add the light to the light list.
|
|
LightSceneInfo->Id = Lights.Add(FLightSceneInfoCompact(LightSceneInfo));
|
|
const FLightSceneInfoCompact& LightSceneInfoCompact = Lights[LightSceneInfo->Id];
|
|
|
|
if (!SimpleDirectionalLight &&
|
|
LightSceneInfo->Proxy->GetLightType() == LightType_Directional &&
|
|
// Only use a stationary or movable light
|
|
!LightSceneInfo->Proxy->HasStaticLighting())
|
|
{
|
|
SimpleDirectionalLight = LightSceneInfo;
|
|
|
|
// if we are forward rendered and this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lightingpolicy
|
|
bScenesPrimitivesNeedStaticMeshElementUpdate = bScenesPrimitivesNeedStaticMeshElementUpdate || (!ShouldUseDeferredRenderer() && !SimpleDirectionalLight->Proxy->HasStaticShadowing());
|
|
}
|
|
|
|
if (LightSceneInfo->Proxy->IsUsedAsAtmosphereSunLight() &&
|
|
(!SunLight || LightSceneInfo->Proxy->GetColor().ComputeLuminance() > SunLight->Proxy->GetColor().ComputeLuminance()) ) // choose brightest sun light...
|
|
{
|
|
SunLight = LightSceneInfo;
|
|
}
|
|
|
|
// Add the light to the scene.
|
|
LightSceneInfo->AddToScene();
|
|
}
|
|
|
|
void FScene::AddLight(ULightComponent* Light)
|
|
{
|
|
// Create the light's scene proxy.
|
|
FLightSceneProxy* Proxy = Light->CreateSceneProxy();
|
|
if(Proxy)
|
|
{
|
|
// Associate the proxy with the light.
|
|
Light->SceneProxy = Proxy;
|
|
|
|
// Update the light's transform and position.
|
|
Proxy->SetTransform(Light->ComponentToWorld.ToMatrixNoScale(),Light->GetLightPosition());
|
|
|
|
// Create the light scene info.
|
|
Proxy->LightSceneInfo = new FLightSceneInfo(Proxy, true);
|
|
|
|
INC_DWORD_STAT(STAT_SceneLights);
|
|
|
|
// Adding a new light
|
|
++NumVisibleLights_GameThread;
|
|
|
|
// Send a command to the rendering thread to add the light to the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddLightCommand,
|
|
FScene*,Scene,this,
|
|
FLightSceneInfo*,LightSceneInfo,Proxy->LightSceneInfo,
|
|
{
|
|
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
|
|
Scene->AddLightSceneInfo_RenderThread(LightSceneInfo);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::AddInvisibleLight(ULightComponent* Light)
|
|
{
|
|
// Create the light's scene proxy.
|
|
FLightSceneProxy* Proxy = Light->CreateSceneProxy();
|
|
|
|
if(Proxy)
|
|
{
|
|
// Associate the proxy with the light.
|
|
Light->SceneProxy = Proxy;
|
|
|
|
// Update the light's transform and position.
|
|
Proxy->SetTransform(Light->ComponentToWorld.ToMatrixNoScale(),Light->GetLightPosition());
|
|
|
|
// Create the light scene info.
|
|
Proxy->LightSceneInfo = new FLightSceneInfo(Proxy, false);
|
|
|
|
INC_DWORD_STAT(STAT_SceneLights);
|
|
|
|
// Send a command to the rendering thread to add the light to the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddLightCommand,
|
|
FScene*,Scene,this,
|
|
FLightSceneInfo*,LightSceneInfo,Proxy->LightSceneInfo,
|
|
{
|
|
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
|
|
LightSceneInfo->Id = Scene->InvisibleLights.Add(FLightSceneInfoCompact(LightSceneInfo));
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::SetSkyLight(FSkyLightSceneProxy* LightProxy)
|
|
{
|
|
check(LightProxy);
|
|
NumEnabledSkylights_GameThread++;
|
|
|
|
// Send a command to the rendering thread to add the light to the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FSetSkyLightCommand,
|
|
FScene*,Scene,this,
|
|
FSkyLightSceneProxy*,LightProxy,LightProxy,
|
|
{
|
|
check(!Scene->SkyLightStack.Contains(LightProxy));
|
|
Scene->SkyLightStack.Push(LightProxy);
|
|
const bool bHadSkylight = Scene->SkyLight != NULL;
|
|
|
|
// Use the most recently enabled skylight
|
|
Scene->SkyLight = LightProxy;
|
|
|
|
if (!bHadSkylight)
|
|
{
|
|
// Mark the scene as needing static draw lists to be recreated if needed
|
|
// The base pass chooses shaders based on whether there's a skylight in the scene, and that is cached in static draw lists
|
|
Scene->bScenesPrimitivesNeedStaticMeshElementUpdate = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
void FScene::DisableSkyLight(FSkyLightSceneProxy* LightProxy)
|
|
{
|
|
check(LightProxy);
|
|
NumEnabledSkylights_GameThread--;
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FDisableSkyLightCommand,
|
|
FScene*,Scene,this,
|
|
FSkyLightSceneProxy*,LightProxy,LightProxy,
|
|
{
|
|
const bool bHadSkylight = Scene->SkyLight != NULL;
|
|
|
|
Scene->SkyLightStack.RemoveSingle(LightProxy);
|
|
|
|
if (Scene->SkyLightStack.Num() > 0)
|
|
{
|
|
// Use the most recently enabled skylight
|
|
Scene->SkyLight = Scene->SkyLightStack.Last();
|
|
}
|
|
else
|
|
{
|
|
Scene->SkyLight = NULL;
|
|
}
|
|
|
|
// Update the scene if we switched skylight enabled states
|
|
if ((Scene->SkyLight != NULL) != bHadSkylight)
|
|
{
|
|
Scene->bScenesPrimitivesNeedStaticMeshElementUpdate = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
void FScene::AddOrRemoveDecal_RenderThread(FDeferredDecalProxy* Proxy, bool bAdd)
|
|
{
|
|
if(bAdd)
|
|
{
|
|
Decals.Add(Proxy);
|
|
}
|
|
else
|
|
{
|
|
// can be optimized
|
|
for(TSparseArray<FDeferredDecalProxy*>::TIterator It(Decals); It; ++It)
|
|
{
|
|
FDeferredDecalProxy* CurrentProxy = *It;
|
|
|
|
if (CurrentProxy == Proxy)
|
|
{
|
|
It.RemoveCurrent();
|
|
delete CurrentProxy;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FScene::AddDecal(UDecalComponent* Component)
|
|
{
|
|
if(!Component->SceneProxy)
|
|
{
|
|
// Create the decals's scene proxy.
|
|
Component->SceneProxy = Component->CreateSceneProxy();
|
|
|
|
INC_DWORD_STAT(STAT_SceneDecals);
|
|
|
|
// Send a command to the rendering thread to add the light to the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddDecalCommand,
|
|
FScene*,Scene,this,
|
|
FDeferredDecalProxy*,Proxy,Component->SceneProxy,
|
|
{
|
|
Scene->AddOrRemoveDecal_RenderThread(Proxy, true);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::RemoveDecal(UDecalComponent* Component)
|
|
{
|
|
if(Component->SceneProxy)
|
|
{
|
|
DEC_DWORD_STAT(STAT_SceneDecals);
|
|
|
|
// Send a command to the rendering thread to remove the light from the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveDecalCommand,
|
|
FScene*,Scene,this,
|
|
FDeferredDecalProxy*,Proxy,Component->SceneProxy,
|
|
{
|
|
Scene->AddOrRemoveDecal_RenderThread(Proxy, false);
|
|
});
|
|
|
|
// Disassociate the primitive's scene proxy.
|
|
Component->SceneProxy = NULL;
|
|
}
|
|
}
|
|
|
|
void FScene::UpdateDecalTransform(UDecalComponent* Decal)
|
|
{
|
|
if(Decal->SceneProxy)
|
|
{
|
|
//Send command to the rendering thread to update the decal's transform.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
UpdateTransformCommand,
|
|
FDeferredDecalProxy*,DecalSceneProxy,Decal->SceneProxy,
|
|
FTransform,ComponentToWorld,Decal->GetComponentToWorld(),
|
|
{
|
|
// Update the primitive's transform.
|
|
DecalSceneProxy->SetTransform(ComponentToWorld);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::AddReflectionCapture(UReflectionCaptureComponent* Component)
|
|
{
|
|
if (!Component->SceneProxy)
|
|
{
|
|
Component->SceneProxy = Component->CreateSceneProxy();
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddCaptureCommand,
|
|
FScene*,Scene,this,
|
|
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
|
|
{
|
|
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
|
|
const int32 PackedIndex = Scene->ReflectionSceneData.RegisteredReflectionCaptures.Add(Proxy);
|
|
|
|
Proxy->PackedIndex = PackedIndex;
|
|
Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.Add(Proxy->Position);
|
|
|
|
checkSlow(Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() == Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.Num());
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::RemoveReflectionCapture(UReflectionCaptureComponent* Component)
|
|
{
|
|
if (Component->SceneProxy)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveCaptureCommand,
|
|
FScene*,Scene,this,
|
|
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
|
|
{
|
|
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
|
|
|
|
int32 CaptureIndex = Proxy->PackedIndex;
|
|
Scene->ReflectionSceneData.RegisteredReflectionCaptures.RemoveAtSwap(CaptureIndex);
|
|
Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.RemoveAtSwap(CaptureIndex);
|
|
|
|
if (Scene->ReflectionSceneData.RegisteredReflectionCaptures.IsValidIndex(CaptureIndex))
|
|
{
|
|
FReflectionCaptureProxy* OtherCapture = Scene->ReflectionSceneData.RegisteredReflectionCaptures[CaptureIndex];
|
|
OtherCapture->PackedIndex = CaptureIndex;
|
|
}
|
|
|
|
delete Proxy;
|
|
|
|
checkSlow(Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() == Scene->ReflectionSceneData.RegisteredReflectionCapturePositions.Num());
|
|
});
|
|
|
|
// Disassociate the primitive's scene proxy.
|
|
Component->SceneProxy = NULL;
|
|
}
|
|
}
|
|
|
|
void FScene::UpdateReflectionCaptureTransform(UReflectionCaptureComponent* Component)
|
|
{
|
|
if (Component->SceneProxy)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
UpdateTransformCommand,
|
|
FReflectionCaptureProxy*,Proxy,Component->SceneProxy,
|
|
FMatrix,Transform,Component->ComponentToWorld.ToMatrixWithScale(),
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
|
|
Proxy->SetTransform(Transform);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::ReleaseReflectionCubemap(UReflectionCaptureComponent* CaptureComponent)
|
|
{
|
|
for (TSparseArray<UReflectionCaptureComponent*>::TIterator It(ReflectionSceneData.AllocatedReflectionCapturesGameThread); It; ++It)
|
|
{
|
|
UReflectionCaptureComponent* CurrentCapture = *It;
|
|
|
|
if (CurrentCapture == CaptureComponent)
|
|
{
|
|
It.RemoveCurrent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
RemoveCaptureCommand,
|
|
UReflectionCaptureComponent*,Component,CaptureComponent,
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Remove(Component);
|
|
});
|
|
}
|
|
|
|
const FReflectionCaptureProxy* FScene::FindClosestReflectionCapture(FVector Position) const
|
|
{
|
|
checkSlow(IsInParallelRenderingThread());
|
|
int32 ClosestCaptureIndex = INDEX_NONE;
|
|
float ClosestDistanceSquared = FLT_MAX;
|
|
|
|
// Linear search through the scene's reflection captures
|
|
// ReflectionSceneData.RegisteredReflectionCapturePositions has been packed densely to make this coherent in memory
|
|
for (int32 CaptureIndex = 0; CaptureIndex < ReflectionSceneData.RegisteredReflectionCapturePositions.Num(); CaptureIndex++)
|
|
{
|
|
const float DistanceSquared = (ReflectionSceneData.RegisteredReflectionCapturePositions[CaptureIndex] - Position).SizeSquared();
|
|
|
|
if (DistanceSquared < ClosestDistanceSquared)
|
|
{
|
|
ClosestDistanceSquared = DistanceSquared;
|
|
ClosestCaptureIndex = CaptureIndex;
|
|
}
|
|
}
|
|
|
|
return ClosestCaptureIndex != INDEX_NONE ? ReflectionSceneData.RegisteredReflectionCaptures[ClosestCaptureIndex] : NULL;
|
|
}
|
|
|
|
void FScene::GetCaptureParameters(const FReflectionCaptureProxy* ReflectionProxy, FTextureRHIParamRef& ReflectionCubemapArray, int32& ArrayIndex) const
|
|
{
|
|
ERHIFeatureLevel::Type LocalFeatureLevel = GetFeatureLevel();
|
|
|
|
if (LocalFeatureLevel >= ERHIFeatureLevel::SM5)
|
|
{
|
|
const FCaptureComponentSceneState* FoundState = ReflectionSceneData.AllocatedReflectionCaptureState.Find(ReflectionProxy->Component);
|
|
|
|
if (FoundState)
|
|
{
|
|
ReflectionCubemapArray = ReflectionSceneData.CubemapArray.GetRenderTarget().ShaderResourceTexture;
|
|
ArrayIndex = FoundState->CaptureIndex;
|
|
}
|
|
}
|
|
else if (ReflectionProxy->SM4FullHDRCubemap)
|
|
{
|
|
ReflectionCubemapArray = ReflectionProxy->SM4FullHDRCubemap->TextureRHI;
|
|
ArrayIndex = 0;
|
|
}
|
|
}
|
|
|
|
void FScene::AddPrecomputedLightVolume(const FPrecomputedLightVolume* Volume)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
AddVolumeCommand,
|
|
const FPrecomputedLightVolume*,Volume,Volume,
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->PrecomputedLightVolumes.Add(Volume);
|
|
Scene->IndirectLightingCache.SetLightingCacheDirty();
|
|
});
|
|
}
|
|
|
|
void FScene::RemovePrecomputedLightVolume(const FPrecomputedLightVolume* Volume)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
RemoveVolumeCommand,
|
|
const FPrecomputedLightVolume*,Volume,Volume,
|
|
FScene*,Scene,this,
|
|
{
|
|
Scene->PrecomputedLightVolumes.Remove(Volume);
|
|
Scene->IndirectLightingCache.SetLightingCacheDirty();
|
|
});
|
|
}
|
|
|
|
struct FUpdateLightTransformParameters
|
|
{
|
|
FMatrix LightToWorld;
|
|
FVector4 Position;
|
|
};
|
|
|
|
void FScene::UpdateLightTransform_RenderThread(FLightSceneInfo* LightSceneInfo, const FUpdateLightTransformParameters& Parameters)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_UpdateSceneLightTime);
|
|
if( LightSceneInfo && LightSceneInfo->bVisible )
|
|
{
|
|
// Don't remove directional lights when their transform changes as nothing in RemoveFromScene() depends on their transform
|
|
if (!(LightSceneInfo->Proxy->GetLightType() == LightType_Directional))
|
|
{
|
|
// Remove the light from the scene.
|
|
LightSceneInfo->RemoveFromScene();
|
|
}
|
|
|
|
// Update the light's transform and position.
|
|
LightSceneInfo->Proxy->SetTransform(Parameters.LightToWorld,Parameters.Position);
|
|
|
|
// Also update the LightSceneInfoCompact
|
|
if( LightSceneInfo->Id != INDEX_NONE )
|
|
{
|
|
LightSceneInfo->Scene->Lights[LightSceneInfo->Id].Init(LightSceneInfo);
|
|
|
|
// Don't re-add directional lights when their transform changes as nothing in AddToScene() depends on their transform
|
|
if (!(LightSceneInfo->Proxy->GetLightType() == LightType_Directional))
|
|
{
|
|
// Add the light to the scene at its new location.
|
|
LightSceneInfo->AddToScene();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FScene::UpdateLightTransform(ULightComponent* Light)
|
|
{
|
|
if(Light->SceneProxy)
|
|
{
|
|
FUpdateLightTransformParameters Parameters;
|
|
Parameters.LightToWorld = Light->ComponentToWorld.ToMatrixNoScale();
|
|
Parameters.Position = Light->GetLightPosition();
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
UpdateLightTransform,
|
|
FScene*,Scene,this,
|
|
FLightSceneInfo*,LightSceneInfo,Light->SceneProxy->GetLightSceneInfo(),
|
|
FUpdateLightTransformParameters,Parameters,Parameters,
|
|
{
|
|
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
|
|
Scene->UpdateLightTransform_RenderThread(LightSceneInfo, Parameters);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the color and brightness of a light which has already been added to the scene.
|
|
*
|
|
* @param Light - light component to update
|
|
*/
|
|
void FScene::UpdateLightColorAndBrightness(ULightComponent* Light)
|
|
{
|
|
if(Light->SceneProxy)
|
|
{
|
|
struct FUpdateLightColorParameters
|
|
{
|
|
FLinearColor NewColor;
|
|
float NewIndirectLightingScale;
|
|
};
|
|
|
|
FUpdateLightColorParameters NewParameters;
|
|
NewParameters.NewColor = FLinearColor(Light->LightColor) * Light->ComputeLightBrightness();
|
|
NewParameters.NewIndirectLightingScale = Light->IndirectLightingIntensity;
|
|
|
|
if( Light->bUseTemperature )
|
|
{
|
|
NewParameters.NewColor *= FLinearColor::MakeFromColorTemperature(Light->Temperature);
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
UpdateLightColorAndBrightness,
|
|
FLightSceneInfo*,LightSceneInfo,Light->SceneProxy->GetLightSceneInfo(),
|
|
FScene*,Scene,this,
|
|
FUpdateLightColorParameters,Parameters,NewParameters,
|
|
{
|
|
if( LightSceneInfo && LightSceneInfo->bVisible )
|
|
{
|
|
LightSceneInfo->Proxy->SetColor(Parameters.NewColor);
|
|
LightSceneInfo->Proxy->IndirectLightingScale = Parameters.NewIndirectLightingScale;
|
|
|
|
// Also update the LightSceneInfoCompact
|
|
if( LightSceneInfo->Id != INDEX_NONE )
|
|
{
|
|
Scene->Lights[ LightSceneInfo->Id ].Color = Parameters.NewColor;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/** Updates the scene's dynamic skylight. */
|
|
void FScene::UpdateDynamicSkyLight(const FLinearColor& UpperColor, const FLinearColor& LowerColor)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
UpdateDynamicSkyLight,
|
|
FScene*,Scene,this,
|
|
FLinearColor,UpperColor,UpperColor,
|
|
FLinearColor,LowerColor,LowerColor,
|
|
{
|
|
Scene->UpperDynamicSkylightColor = UpperColor;
|
|
Scene->LowerDynamicSkylightColor = LowerColor;
|
|
});
|
|
}
|
|
|
|
void FScene::RemoveLightSceneInfo_RenderThread(FLightSceneInfo* LightSceneInfo)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_RemoveSceneLightTime);
|
|
|
|
if (LightSceneInfo->bVisible)
|
|
{
|
|
if (LightSceneInfo == SimpleDirectionalLight)
|
|
{
|
|
// if we are forward rendered and this light is a dynamic shadowcast then we need to update the static draw lists to pick a new lightingpolicy
|
|
bScenesPrimitivesNeedStaticMeshElementUpdate = bScenesPrimitivesNeedStaticMeshElementUpdate || (!ShouldUseDeferredRenderer() && !SimpleDirectionalLight->Proxy->HasStaticShadowing());
|
|
SimpleDirectionalLight = NULL;
|
|
}
|
|
|
|
if (LightSceneInfo == SunLight)
|
|
{
|
|
SunLight = NULL;
|
|
// Search for new sun light...
|
|
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator It(Lights); It; ++It)
|
|
{
|
|
const FLightSceneInfoCompact& LightInfo = *It;
|
|
if (LightInfo.LightSceneInfo != LightSceneInfo
|
|
&& LightInfo.LightSceneInfo->Proxy->bUsedAsAtmosphereSunLight
|
|
&& (!SunLight || SunLight->Proxy->GetColor().ComputeLuminance() < LightInfo.LightSceneInfo->Proxy->GetColor().ComputeLuminance()) )
|
|
{
|
|
SunLight = LightInfo.LightSceneInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the light from the scene.
|
|
LightSceneInfo->RemoveFromScene();
|
|
|
|
// Remove the light from the lights list.
|
|
Lights.RemoveAt(LightSceneInfo->Id);
|
|
}
|
|
else
|
|
{
|
|
InvisibleLights.RemoveAt(LightSceneInfo->Id);
|
|
}
|
|
|
|
// Free the light scene info and proxy.
|
|
delete LightSceneInfo->Proxy;
|
|
delete LightSceneInfo;
|
|
}
|
|
|
|
void FScene::RemoveLight(ULightComponent* Light)
|
|
{
|
|
if(Light->SceneProxy)
|
|
{
|
|
FLightSceneInfo* LightSceneInfo = Light->SceneProxy->GetLightSceneInfo();
|
|
|
|
DEC_DWORD_STAT(STAT_SceneLights);
|
|
|
|
// Removing one visible light
|
|
--NumVisibleLights_GameThread;
|
|
|
|
// Disassociate the primitive's render info.
|
|
Light->SceneProxy = NULL;
|
|
|
|
// Send a command to the rendering thread to remove the light from the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveLightCommand,
|
|
FScene*,Scene,this,
|
|
FLightSceneInfo*,LightSceneInfo,LightSceneInfo,
|
|
{
|
|
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
|
|
Scene->RemoveLightSceneInfo_RenderThread(LightSceneInfo);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::AddExponentialHeightFog(UExponentialHeightFogComponent* FogComponent)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddFogCommand,
|
|
FScene*,Scene,this,
|
|
FExponentialHeightFogSceneInfo,HeightFogSceneInfo,FExponentialHeightFogSceneInfo(FogComponent),
|
|
{
|
|
// Create a FExponentialHeightFogSceneInfo for the component in the scene's fog array.
|
|
new(Scene->ExponentialFogs) FExponentialHeightFogSceneInfo(HeightFogSceneInfo);
|
|
});
|
|
}
|
|
|
|
void FScene::RemoveExponentialHeightFog(UExponentialHeightFogComponent* FogComponent)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveFogCommand,
|
|
FScene*,Scene,this,
|
|
UExponentialHeightFogComponent*,FogComponent,FogComponent,
|
|
{
|
|
// Remove the given component's FExponentialHeightFogSceneInfo from the scene's fog array.
|
|
for(int32 FogIndex = 0;FogIndex < Scene->ExponentialFogs.Num();FogIndex++)
|
|
{
|
|
if(Scene->ExponentialFogs[FogIndex].Component == FogComponent)
|
|
{
|
|
Scene->ExponentialFogs.RemoveAt(FogIndex);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void FScene::AddAtmosphericFog(UAtmosphericFogComponent* FogComponent)
|
|
{
|
|
check(FogComponent);
|
|
|
|
FAtmosphericFogSceneInfo* FogSceneInfo = new FAtmosphericFogSceneInfo(FogComponent, this);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddAtmosphericFogCommand,
|
|
FScene*,Scene,this,
|
|
FAtmosphericFogSceneInfo*,FogSceneInfo,FogSceneInfo,
|
|
{
|
|
if (Scene->AtmosphericFog && Scene->AtmosphericFog->Component != FogSceneInfo->Component)
|
|
{
|
|
delete Scene->AtmosphericFog;
|
|
Scene->AtmosphericFog = NULL;
|
|
}
|
|
|
|
if (Scene->AtmosphericFog == NULL)
|
|
{
|
|
Scene->AtmosphericFog = FogSceneInfo;
|
|
}
|
|
else
|
|
{
|
|
delete FogSceneInfo;
|
|
}
|
|
});
|
|
}
|
|
|
|
void FScene::RemoveAtmosphericFog(UAtmosphericFogComponent* FogComponent)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveAtmosphericFogCommand,
|
|
FScene*,Scene,this,
|
|
UAtmosphericFogComponent*,FogComponent,FogComponent,
|
|
{
|
|
// Remove the given component's FExponentialHeightFogSceneInfo from the scene's fog array.
|
|
if (Scene->AtmosphericFog && Scene->AtmosphericFog->Component == FogComponent)
|
|
{
|
|
delete Scene->AtmosphericFog;
|
|
Scene->AtmosphericFog = NULL;
|
|
}
|
|
});
|
|
}
|
|
|
|
void FScene::AddWindSource(UWindDirectionalSourceComponent* WindComponent)
|
|
{
|
|
// if this wind component is not activated (or Auto Active is set to false), then don't add to WindSources
|
|
if(!WindComponent->IsActive())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FWindSourceSceneProxy* SceneProxy = WindComponent->CreateSceneProxy();
|
|
WindComponent->SceneProxy = SceneProxy;
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FAddWindSourceCommand,
|
|
FScene*,Scene,this,
|
|
FWindSourceSceneProxy*,SceneProxy,SceneProxy,
|
|
{
|
|
Scene->WindSources.Add(SceneProxy);
|
|
});
|
|
}
|
|
|
|
void FScene::RemoveWindSource(UWindDirectionalSourceComponent* WindComponent)
|
|
{
|
|
FWindSourceSceneProxy* SceneProxy = WindComponent->SceneProxy;
|
|
WindComponent->SceneProxy = NULL;
|
|
|
|
if(SceneProxy)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FRemoveWindSourceCommand,
|
|
FScene*,Scene,this,
|
|
FWindSourceSceneProxy*,SceneProxy,SceneProxy,
|
|
{
|
|
Scene->WindSources.Remove(SceneProxy);
|
|
|
|
delete SceneProxy;
|
|
});
|
|
}
|
|
}
|
|
|
|
const TArray<FWindSourceSceneProxy*>& FScene::GetWindSources_RenderThread() const
|
|
{
|
|
checkSlow(IsInRenderingThread());
|
|
return WindSources;
|
|
}
|
|
|
|
void FScene::GetWindParameters(const FVector& Position, FVector& OutDirection, float& OutSpeed, float& OutMinGustAmt, float& OutMaxGustAmt) const
|
|
{
|
|
FWindSourceSceneProxy::FWindData AccumWindData;
|
|
AccumWindData.PrepareForAccumulate();
|
|
|
|
int32 NumActiveWindSources = 0;
|
|
FVector4 AccumulatedDirectionAndSpeed(0,0,0,0);
|
|
float TotalWeight = 0.0f;
|
|
for (int32 i = 0; i < WindSources.Num(); i++)
|
|
{
|
|
|
|
FVector4 CurrentDirectionAndSpeed;
|
|
float Weight;
|
|
const FWindSourceSceneProxy* CurrentSource = WindSources[i];
|
|
FWindSourceSceneProxy::FWindData CurrentSourceData;
|
|
if (CurrentSource->GetWindParameters(Position, CurrentSourceData, Weight))
|
|
{
|
|
AccumWindData.AddWeighted(CurrentSourceData, Weight);
|
|
TotalWeight += Weight;
|
|
NumActiveWindSources++;
|
|
}
|
|
}
|
|
|
|
AccumWindData.NormalizeByTotalWeight(TotalWeight);
|
|
|
|
if (NumActiveWindSources == 0)
|
|
{
|
|
AccumWindData.Direction = FVector(1.0f, 0.0f, 0.0f);
|
|
}
|
|
OutDirection = AccumWindData.Direction;
|
|
OutSpeed = AccumWindData.Speed;
|
|
OutMinGustAmt = AccumWindData.MinGustAmt;
|
|
OutMaxGustAmt = AccumWindData.MaxGustAmt;
|
|
}
|
|
|
|
void FScene::GetDirectionalWindParameters(FVector& OutDirection, float& OutSpeed, float& OutMinGustAmt, float& OutMaxGustAmt) const
|
|
{
|
|
FWindSourceSceneProxy::FWindData AccumWindData;
|
|
AccumWindData.PrepareForAccumulate();
|
|
|
|
int32 NumActiveWindSources = 0;
|
|
FVector4 AccumulatedDirectionAndSpeed(0,0,0,0);
|
|
float TotalWeight = 0.0f;
|
|
for (int32 i = 0; i < WindSources.Num(); i++)
|
|
{
|
|
FVector4 CurrentDirectionAndSpeed;
|
|
float Weight;
|
|
const FWindSourceSceneProxy* CurrentSource = WindSources[i];
|
|
FWindSourceSceneProxy::FWindData CurrentSourceData;
|
|
if (CurrentSource->GetDirectionalWindParameters(CurrentSourceData, Weight))
|
|
{
|
|
AccumWindData.AddWeighted(CurrentSourceData, Weight);
|
|
TotalWeight += Weight;
|
|
NumActiveWindSources++;
|
|
}
|
|
}
|
|
|
|
AccumWindData.NormalizeByTotalWeight(TotalWeight);
|
|
|
|
if (NumActiveWindSources == 0)
|
|
{
|
|
AccumWindData.Direction = FVector(1.0f, 0.0f, 0.0f);
|
|
}
|
|
OutDirection = AccumWindData.Direction;
|
|
OutSpeed = AccumWindData.Speed;
|
|
OutMinGustAmt = AccumWindData.MinGustAmt;
|
|
OutMaxGustAmt = AccumWindData.MaxGustAmt;
|
|
}
|
|
|
|
void FScene::AddSpeedTreeWind(FVertexFactory* VertexFactory, const UStaticMesh* StaticMesh)
|
|
{
|
|
if (StaticMesh != NULL && StaticMesh->SpeedTreeWind.IsValid() && StaticMesh->RenderData.IsValid())
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FAddSpeedTreeWindCommand,
|
|
FScene*,Scene,this,
|
|
const UStaticMesh*,StaticMesh,StaticMesh,
|
|
FVertexFactory*,VertexFactory,VertexFactory,
|
|
{
|
|
Scene->SpeedTreeVertexFactoryMap.Add(VertexFactory, StaticMesh);
|
|
|
|
if (Scene->SpeedTreeWindComputationMap.Contains(StaticMesh))
|
|
{
|
|
(*(Scene->SpeedTreeWindComputationMap.Find(StaticMesh)))->ReferenceCount++;
|
|
}
|
|
else
|
|
{
|
|
FSpeedTreeWindComputation* WindComputation = new FSpeedTreeWindComputation;
|
|
WindComputation->Wind = *(StaticMesh->SpeedTreeWind.Get( ));
|
|
WindComputation->UniformBuffer.InitResource();
|
|
Scene->SpeedTreeWindComputationMap.Add(StaticMesh, WindComputation);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::RemoveSpeedTreeWind(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh)
|
|
{
|
|
if (StaticMesh != NULL && StaticMesh->SpeedTreeWind.IsValid() && StaticMesh->RenderData.IsValid())
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FRemoveSpeedTreeWindCommand,
|
|
FScene*,Scene,this,
|
|
const UStaticMesh*, StaticMesh, StaticMesh,
|
|
FVertexFactory*,VertexFactory,VertexFactory,
|
|
{
|
|
Scene->RemoveSpeedTreeWind_RenderThread(VertexFactory, StaticMesh);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FScene::RemoveSpeedTreeWind_RenderThread(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh)
|
|
{
|
|
FSpeedTreeWindComputation** WindComputationRef = SpeedTreeWindComputationMap.Find(StaticMesh);
|
|
if (WindComputationRef != NULL)
|
|
{
|
|
FSpeedTreeWindComputation* WindComputation = *WindComputationRef;
|
|
|
|
WindComputation->ReferenceCount--;
|
|
if (WindComputation->ReferenceCount < 1)
|
|
{
|
|
for (auto Iter = SpeedTreeVertexFactoryMap.CreateIterator(); Iter; ++Iter )
|
|
{
|
|
if (Iter.Value() == StaticMesh)
|
|
{
|
|
Iter.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
SpeedTreeWindComputationMap.Remove(StaticMesh);
|
|
WindComputation->UniformBuffer.ReleaseResource();
|
|
delete WindComputation;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FScene::UpdateSpeedTreeWind(double CurrentTime)
|
|
{
|
|
#define SET_SPEEDTREE_TABLE_FLOAT4V(name, offset) \
|
|
UniformParameters.name = *(FVector4*)(WindShaderValues + FSpeedTreeWind::offset); \
|
|
UniformParameters.Prev##name = *(FVector4*)(WindShaderValues + FSpeedTreeWind::offset + FSpeedTreeWind::NUM_SHADER_VALUES);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FUpdateSpeedTreeWindCommand,
|
|
FScene*,Scene,this,
|
|
double,CurrentTime,CurrentTime,
|
|
{
|
|
FVector WindDirection;
|
|
float WindSpeed;
|
|
float WindMinGustAmt;
|
|
float WindMaxGustAmt;
|
|
Scene->GetDirectionalWindParameters(WindDirection, WindSpeed, WindMinGustAmt, WindMaxGustAmt);
|
|
|
|
for (TMap<const UStaticMesh*, FSpeedTreeWindComputation*>::TIterator It(Scene->SpeedTreeWindComputationMap); It; ++It )
|
|
{
|
|
const UStaticMesh* StaticMesh = It.Key();
|
|
FSpeedTreeWindComputation* WindComputation = It.Value();
|
|
|
|
if( !StaticMesh->RenderData )
|
|
{
|
|
It.RemoveCurrent();
|
|
continue;
|
|
}
|
|
|
|
if (GIsEditor && StaticMesh->SpeedTreeWind->NeedsReload( ))
|
|
{
|
|
// reload the wind since it may have changed or been scaled differently during reimport
|
|
StaticMesh->SpeedTreeWind->SetNeedsReload(false);
|
|
WindComputation->Wind = *(StaticMesh->SpeedTreeWind.Get( ));
|
|
|
|
// make sure the vertex factories are registered (sometimes goes wrong during a reimport)
|
|
for (int32 LODIndex = 0; LODIndex < StaticMesh->RenderData->LODResources.Num(); ++LODIndex)
|
|
{
|
|
Scene->SpeedTreeVertexFactoryMap.Add(&StaticMesh->RenderData->LODResources[LODIndex].VertexFactory, StaticMesh);
|
|
}
|
|
}
|
|
|
|
// advance the wind object
|
|
WindComputation->Wind.SetDirection(WindDirection);
|
|
WindComputation->Wind.SetStrength(WindSpeed);
|
|
WindComputation->Wind.SetGustMin(WindMinGustAmt);
|
|
WindComputation->Wind.SetGustMax(WindMaxGustAmt);
|
|
WindComputation->Wind.Advance(true, CurrentTime);
|
|
|
|
// copy data into uniform buffer
|
|
const float* WindShaderValues = WindComputation->Wind.GetShaderTable();
|
|
|
|
FSpeedTreeUniformParameters UniformParameters;
|
|
UniformParameters.WindAnimation.Set(CurrentTime, 0.0f, 0.0f, 0.0f);
|
|
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindVector, SH_WIND_DIR_X);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindGlobal, SH_GLOBAL_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindBranch, SH_BRANCH_1_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindBranchTwitch, SH_BRANCH_1_TWITCH);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindBranchWhip, SH_BRANCH_1_WHIP);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindBranchAnchor, SH_WIND_ANCHOR_X);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindBranchAdherences, SH_GLOBAL_DIRECTION_ADHERENCE);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindTurbulences, SH_BRANCH_1_TURBULENCE);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf1Ripple, SH_LEAF_1_RIPPLE_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf1Tumble, SH_LEAF_1_TUMBLE_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf1Twitch, SH_LEAF_1_TWITCH_THROW);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf2Ripple, SH_LEAF_2_RIPPLE_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf2Tumble, SH_LEAF_2_TUMBLE_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindLeaf2Twitch, SH_LEAF_2_TWITCH_THROW);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindFrondRipple, SH_FROND_RIPPLE_TIME);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindRollingBranch, SH_ROLLING_BRANCH_FIELD_MIN);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindRollingLeafAndDirection, SH_ROLLING_LEAF_RIPPLE_MIN);
|
|
SET_SPEEDTREE_TABLE_FLOAT4V(WindRollingNoise, SH_ROLLING_NOISE_PERIOD);
|
|
|
|
WindComputation->UniformBuffer.SetContents(UniformParameters);
|
|
}
|
|
});
|
|
#undef SET_SPEEDTREE_TABLE_FLOAT4V
|
|
}
|
|
|
|
FUniformBufferRHIParamRef FScene::GetSpeedTreeUniformBuffer(const FVertexFactory* VertexFactory)
|
|
{
|
|
if (VertexFactory != NULL)
|
|
{
|
|
const UStaticMesh** StaticMesh = SpeedTreeVertexFactoryMap.Find(VertexFactory);
|
|
if (StaticMesh != NULL)
|
|
{
|
|
FSpeedTreeWindComputation** WindComputation = SpeedTreeWindComputationMap.Find(*StaticMesh);
|
|
if (WindComputation != NULL)
|
|
{
|
|
return (*WindComputation)->UniformBuffer.GetUniformBufferRHI();
|
|
}
|
|
}
|
|
}
|
|
|
|
return FUniformBufferRHIParamRef();
|
|
}
|
|
|
|
/**
|
|
* Retrieves the lights interacting with the passed in primitive and adds them to the out array.
|
|
*
|
|
* Render thread version of function.
|
|
*
|
|
* @param Primitive Primitive to retrieve interacting lights for
|
|
* @param RelevantLights [out] Array of lights interacting with primitive
|
|
*/
|
|
void FScene::GetRelevantLights_RenderThread( UPrimitiveComponent* Primitive, TArray<const ULightComponent*>* RelevantLights ) const
|
|
{
|
|
check( Primitive );
|
|
check( RelevantLights );
|
|
if( Primitive->SceneProxy )
|
|
{
|
|
for( const FLightPrimitiveInteraction* Interaction=Primitive->SceneProxy->GetPrimitiveSceneInfo()->LightList; Interaction; Interaction=Interaction->GetNextLight() )
|
|
{
|
|
RelevantLights->Add( Interaction->GetLight()->Proxy->GetLightComponent() );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the lights interacting with the passed in primitive and adds them to the out array.
|
|
*
|
|
* @param Primitive Primitive to retrieve interacting lights for
|
|
* @param RelevantLights [out] Array of lights interacting with primitive
|
|
*/
|
|
void FScene::GetRelevantLights( UPrimitiveComponent* Primitive, TArray<const ULightComponent*>* RelevantLights ) const
|
|
{
|
|
if( Primitive && RelevantLights )
|
|
{
|
|
// Add interacting lights to the array.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FGetRelevantLightsCommand,
|
|
const FScene*,Scene,this,
|
|
UPrimitiveComponent*,Primitive,Primitive,
|
|
TArray<const ULightComponent*>*,RelevantLights,RelevantLights,
|
|
{
|
|
Scene->GetRelevantLights_RenderThread( Primitive, RelevantLights );
|
|
});
|
|
|
|
// We need to block the main thread as the rendering thread needs to finish modifying the array before we can continue.
|
|
FlushRenderingCommands();
|
|
}
|
|
}
|
|
|
|
/** Sets the precomputed visibility handler for the scene, or NULL to clear the current one. */
|
|
void FScene::SetPrecomputedVisibility(const FPrecomputedVisibilityHandler* NewPrecomputedVisibilityHandler)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
UpdatePrecomputedVisibility,
|
|
FScene*,Scene,this,
|
|
const FPrecomputedVisibilityHandler*,PrecomputedVisibilityHandler,NewPrecomputedVisibilityHandler,
|
|
{
|
|
Scene->PrecomputedVisibilityHandler = PrecomputedVisibilityHandler;
|
|
});
|
|
}
|
|
|
|
void FScene::SetShaderMapsOnMaterialResources_RenderThread(FRHICommandListImmediate& RHICmdList, const FMaterialsToUpdateMap& MaterialsToUpdate)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_Scene_SetShaderMapsOnMaterialResources_RT);
|
|
|
|
TArray<const FMaterial*> MaterialArray;
|
|
|
|
for (FMaterialsToUpdateMap::TConstIterator It(MaterialsToUpdate); It; ++It)
|
|
{
|
|
FMaterial* Material = It.Key();
|
|
FMaterialShaderMap* ShaderMap = It.Value();
|
|
Material->SetRenderingThreadShaderMap(ShaderMap);
|
|
check(!ShaderMap || ShaderMap->IsValidForRendering());
|
|
MaterialArray.Add(Material);
|
|
}
|
|
|
|
const auto SceneFeatureLevel = GetFeatureLevel();
|
|
bool bFoundAnyInitializedMaterials = false;
|
|
|
|
// Iterate through all loaded material render proxies and recache their uniform expressions if needed
|
|
// This search does not scale well, but is only used when uploading async shader compile results
|
|
for (TSet<FMaterialRenderProxy*>::TConstIterator It(FMaterialRenderProxy::GetMaterialRenderProxyMap()); It; ++It)
|
|
{
|
|
FMaterialRenderProxy* MaterialProxy = *It;
|
|
FMaterial* Material = MaterialProxy->GetMaterialNoFallback(SceneFeatureLevel);
|
|
|
|
if (Material && MaterialsToUpdate.Contains(Material))
|
|
{
|
|
// Materials used as async fallbacks can't be updated through this mechanism and should have been updated synchronously earlier
|
|
check(!Material->RequiresSynchronousCompilation());
|
|
MaterialProxy->CacheUniformExpressions();
|
|
bFoundAnyInitializedMaterials = true;
|
|
|
|
const FMaterial& MaterialForRendering = *MaterialProxy->GetMaterial(SceneFeatureLevel);
|
|
check(MaterialForRendering.GetRenderingThreadShaderMap());
|
|
|
|
check(!MaterialProxy->UniformExpressionCache[SceneFeatureLevel].bUpToDate
|
|
|| MaterialProxy->UniformExpressionCache[SceneFeatureLevel].CachedUniformExpressionShaderMap == MaterialForRendering.GetRenderingThreadShaderMap());
|
|
|
|
check(MaterialForRendering.GetRenderingThreadShaderMap()->IsValidForRendering());
|
|
}
|
|
}
|
|
|
|
// Update static draw lists, which cache shader references from materials, but the shader map has now changed
|
|
if (bFoundAnyInitializedMaterials)
|
|
{
|
|
UpdateStaticDrawListsForMaterials_RenderThread(RHICmdList, MaterialArray);
|
|
}
|
|
}
|
|
|
|
void FScene::SetShaderMapsOnMaterialResources(const TMap<FMaterial*, class FMaterialShaderMap*>& MaterialsToUpdate)
|
|
{
|
|
for (TMap<FMaterial*, FMaterialShaderMap*>::TConstIterator It(MaterialsToUpdate); It; ++It)
|
|
{
|
|
FMaterial* Material = It.Key();
|
|
check(!Material->RequiresSynchronousCompilation());
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FSetShaderMapOnMaterialResources,
|
|
FScene*,Scene,this,
|
|
FMaterialsToUpdateMap,MaterialsToUpdate,MaterialsToUpdate,
|
|
{
|
|
Scene->SetShaderMapsOnMaterialResources_RenderThread(RHICmdList, MaterialsToUpdate);
|
|
});
|
|
}
|
|
|
|
void FScene::UpdateStaticDrawListsForMaterials_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<const FMaterial*>& Materials)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_Scene_UpdateStaticDrawListsForMaterials_RT);
|
|
|
|
// Warning: if any static draw lists are missed here, there will be a crash when trying to render with shaders that have been deleted!
|
|
TArray<FPrimitiveSceneInfo*> PrimitivesToUpdate;
|
|
auto SceneFeatureLevel = GetFeatureLevel();
|
|
|
|
if (ShouldUseDeferredRenderer())
|
|
{
|
|
for (int32 DrawType = 0; DrawType < EBasePass_MAX; DrawType++)
|
|
{
|
|
BasePassNoLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassSimpleDynamicLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassCachedVolumeIndirectLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassCachedPointIndirectLightingDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassHighQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassDistanceFieldShadowMapLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassLowQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassSelfShadowedTranslucencyDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 DrawType = 0; DrawType < EBasePass_MAX; DrawType++)
|
|
{
|
|
BasePassForForwardShadingNoLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingLowQualityLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingMovableDirectionalLightDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingMovableDirectionalLightCSMDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingMovableDirectionalLightLightmapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList[DrawType].GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
}
|
|
}
|
|
|
|
PositionOnlyDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
DepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
MaskedDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
HitProxyDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
HitProxyDrawList_OpaqueOnly.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
VelocityDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
WholeSceneShadowDepthDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
WholeSceneReflectiveShadowMapDrawList.GetUsedPrimitivesBasedOnMaterials(SceneFeatureLevel, Materials, PrimitivesToUpdate);
|
|
|
|
for (int32 PrimitiveIndex = 0; PrimitiveIndex < PrimitivesToUpdate.Num(); PrimitiveIndex++)
|
|
{
|
|
FPrimitiveSceneInfo* Primitive = PrimitivesToUpdate[PrimitiveIndex];
|
|
|
|
Primitive->RemoveStaticMeshes();
|
|
Primitive->AddStaticMeshes(RHICmdList);
|
|
}
|
|
}
|
|
|
|
void FScene::UpdateStaticDrawListsForMaterials(const TArray<const FMaterial*>& Materials)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FUpdateDrawLists,
|
|
FScene*,Scene,this,
|
|
TArray<const FMaterial*>,Materials,Materials,
|
|
{
|
|
Scene->UpdateStaticDrawListsForMaterials_RenderThread(RHICmdList, Materials);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return true if hit proxies should be rendered in this scene.
|
|
*/
|
|
bool FScene::RequiresHitProxies() const
|
|
{
|
|
return (GIsEditor && bRequiresHitProxies);
|
|
}
|
|
|
|
void FScene::Release()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
// Verify that no components reference this scene being destroyed
|
|
static bool bTriggeredOnce = false;
|
|
|
|
if (!bTriggeredOnce)
|
|
{
|
|
for (auto* ActorComponent : TObjectRange<UActorComponent>())
|
|
{
|
|
if ( !ensureMsg(!ActorComponent->IsRegistered() || ActorComponent->GetScene() != this,
|
|
*FString::Printf(TEXT("Component Name: %s World Name: %s Component Mesh: %s"),
|
|
*ActorComponent->GetFullName(),
|
|
*GetWorld()->GetFullName(),
|
|
Cast<UStaticMeshComponent>(ActorComponent) ? *CastChecked<UStaticMeshComponent>(ActorComponent)->StaticMesh->GetFullName() : TEXT("Not a static mesh"))) )
|
|
{
|
|
bTriggeredOnce = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GetRendererModule().RemoveScene(this);
|
|
|
|
// Send a command to the rendering thread to release the scene.
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
FReleaseCommand,
|
|
FScene*,Scene,this,
|
|
{
|
|
delete Scene;
|
|
});
|
|
}
|
|
|
|
void FScene::ConditionalMarkStaticMeshElementsForUpdate()
|
|
{
|
|
static auto* EarlyZPassCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.EarlyZPass"));
|
|
|
|
bool bMobileHDR = IsMobileHDR();
|
|
bool bMobileHDR32bpp = IsMobileHDR32bpp();
|
|
int32 DesiredStaticDrawListsEarlyZPassMode = EarlyZPassCvar->GetValueOnRenderThread();
|
|
|
|
if (bScenesPrimitivesNeedStaticMeshElementUpdate
|
|
|| bStaticDrawListsMobileHDR != bMobileHDR
|
|
|| bStaticDrawListsMobileHDR32bpp != bMobileHDR32bpp
|
|
|| StaticDrawListsEarlyZPassMode != DesiredStaticDrawListsEarlyZPassMode)
|
|
{
|
|
// Mark all primitives as needing an update
|
|
// Note: Only visible primitives will actually update their static mesh elements
|
|
for (int32 PrimitiveIndex = 0; PrimitiveIndex < Primitives.Num(); PrimitiveIndex++)
|
|
{
|
|
Primitives[PrimitiveIndex]->BeginDeferredUpdateStaticMeshes();
|
|
}
|
|
|
|
bScenesPrimitivesNeedStaticMeshElementUpdate = false;
|
|
bStaticDrawListsMobileHDR = bMobileHDR;
|
|
bStaticDrawListsMobileHDR32bpp = bMobileHDR32bpp;
|
|
StaticDrawListsEarlyZPassMode = DesiredStaticDrawListsEarlyZPassMode;
|
|
}
|
|
}
|
|
|
|
void FScene::DumpUnbuiltLightIteractions( FOutputDevice& Ar ) const
|
|
{
|
|
FlushRenderingCommands();
|
|
|
|
TArray<FString> LightsWithUnbuiltInteractions;
|
|
TArray<FString> PrimitivesWithUnbuiltInteractions;
|
|
|
|
// if want to print out all of the lights
|
|
for( TSparseArray<FLightSceneInfoCompact>::TConstIterator It(Lights); It; ++It )
|
|
{
|
|
const FLightSceneInfoCompact& LightCompactInfo = *It;
|
|
FLightSceneInfo* LightSceneInfo = LightCompactInfo.LightSceneInfo;
|
|
|
|
bool bLightHasUnbuiltInteractions = false;
|
|
|
|
for(FLightPrimitiveInteraction* Interaction = LightSceneInfo->DynamicPrimitiveList;
|
|
Interaction;
|
|
Interaction = Interaction->GetNextPrimitive())
|
|
{
|
|
if (Interaction->IsUncachedStaticLighting())
|
|
{
|
|
bLightHasUnbuiltInteractions = true;
|
|
PrimitivesWithUnbuiltInteractions.AddUnique(Interaction->GetPrimitiveSceneInfo()->ComponentForDebuggingOnly->GetFullName());
|
|
}
|
|
}
|
|
|
|
if (bLightHasUnbuiltInteractions)
|
|
{
|
|
LightsWithUnbuiltInteractions.AddUnique(LightSceneInfo->Proxy->GetComponentName().ToString());
|
|
}
|
|
}
|
|
|
|
Ar.Logf( TEXT( "DumpUnbuiltLightIteractions" ) );
|
|
Ar.Logf( TEXT( "Lights with unbuilt interactions: %d" ), LightsWithUnbuiltInteractions.Num() );
|
|
for (int Index = 0; Index < LightsWithUnbuiltInteractions.Num(); Index++)
|
|
{
|
|
Ar.Logf(*(FString(TEXT(" Light ")) + LightsWithUnbuiltInteractions[Index]));
|
|
}
|
|
|
|
Ar.Logf( TEXT( "" ) );
|
|
Ar.Logf( TEXT( "Primitives with unbuilt interactions: %d" ), PrimitivesWithUnbuiltInteractions.Num() );
|
|
for (int Index = 0; Index < PrimitivesWithUnbuiltInteractions.Num(); Index++)
|
|
{
|
|
Ar.Logf(*(FString(TEXT(" Primitive ")) + PrimitivesWithUnbuiltInteractions[Index]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logs the provided draw list stats.
|
|
*/
|
|
static void LogDrawListStats(FDrawListStats Stats, const TCHAR* DrawListName)
|
|
{
|
|
if (Stats.NumDrawingPolicies == 0 || Stats.NumMeshes == 0)
|
|
{
|
|
UE_LOG(LogRenderer,Log,TEXT("%s: empty"), DrawListName);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRenderer,Log,
|
|
TEXT("%s: %d policies %d meshes\n")
|
|
TEXT(" - %d median meshes/policy\n")
|
|
TEXT(" - %f mean meshes/policy\n")
|
|
TEXT(" - %d max meshes/policy\n")
|
|
TEXT(" - %d policies with one mesh"),
|
|
DrawListName,
|
|
Stats.NumDrawingPolicies,
|
|
Stats.NumMeshes,
|
|
Stats.MedianMeshesPerDrawingPolicy,
|
|
(float)Stats.NumMeshes / (float)Stats.NumDrawingPolicies,
|
|
Stats.MaxMeshesPerDrawingPolicy,
|
|
Stats.NumSingleMeshDrawingPolicies
|
|
);
|
|
}
|
|
}
|
|
|
|
void FScene::DumpStaticMeshDrawListStats() const
|
|
{
|
|
UE_LOG(LogRenderer,Log,TEXT("Static mesh draw lists for %s:"),
|
|
World ? *World->GetFullName() : TEXT("[no world]")
|
|
);
|
|
#define DUMP_DRAW_LIST(Name) LogDrawListStats(Name.GetStats(), TEXT(#Name))
|
|
DUMP_DRAW_LIST(PositionOnlyDepthDrawList);
|
|
DUMP_DRAW_LIST(DepthDrawList);
|
|
DUMP_DRAW_LIST(MaskedDepthDrawList);
|
|
DUMP_DRAW_LIST(BasePassNoLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassNoLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassSimpleDynamicLightingDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassSimpleDynamicLightingDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassCachedVolumeIndirectLightingDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassCachedVolumeIndirectLightingDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassCachedPointIndirectLightingDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassCachedPointIndirectLightingDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassHighQualityLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassHighQualityLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassDistanceFieldShadowMapLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassDistanceFieldShadowMapLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassLowQualityLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassLowQualityLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassSelfShadowedTranslucencyDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassSelfShadowedTranslucencyDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[EBasePass_Masked]);
|
|
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingNoLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingNoLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingLowQualityLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingLowQualityLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightCSMDrawList[EBasePass_Default]);
|
|
DUMP_DRAW_LIST(BasePassForForwardShadingMovableDirectionalLightCSMDrawList[EBasePass_Masked]);
|
|
DUMP_DRAW_LIST(HitProxyDrawList);
|
|
DUMP_DRAW_LIST(HitProxyDrawList_OpaqueOnly);
|
|
DUMP_DRAW_LIST(VelocityDrawList);
|
|
DUMP_DRAW_LIST(WholeSceneShadowDepthDrawList);
|
|
#undef DUMP_DRAW_LIST
|
|
}
|
|
|
|
/**
|
|
* Dumps stats for all scenes to the log.
|
|
*/
|
|
static void DumpDrawListStats()
|
|
{
|
|
for (TObjectIterator<UWorld> It; It; ++It)
|
|
{
|
|
UWorld* World = *It;
|
|
if (World && World->Scene)
|
|
{
|
|
World->Scene->DumpStaticMeshDrawListStats();
|
|
}
|
|
}
|
|
}
|
|
|
|
static FAutoConsoleCommand GDumpDrawListStatsCmd(
|
|
TEXT("r.DumpDrawListStats"),
|
|
TEXT("Dumps static mesh draw list statistics for all scenes associated with ")
|
|
TEXT("world objects."),
|
|
FConsoleCommandDelegate::CreateStatic(&DumpDrawListStats)
|
|
);
|
|
|
|
/**
|
|
* Exports the scene.
|
|
*
|
|
* @param Ar The Archive used for exporting.
|
|
**/
|
|
void FScene::Export( FArchive& Ar ) const
|
|
{
|
|
|
|
}
|
|
|
|
void FScene::ApplyWorldOffset(FVector InOffset)
|
|
{
|
|
// Send a command to the rendering thread to shift scene data
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FApplyWorldOffset,
|
|
FScene*,Scene,this,
|
|
FVector,InOffset,InOffset,
|
|
{
|
|
Scene->ApplyWorldOffset_RenderThread(InOffset);
|
|
});
|
|
}
|
|
|
|
// StaticMeshDrawList elements shifting
|
|
template<typename T>
|
|
static void StaticMeshDrawListApplyWorldOffset(T& InList, FVector InOffset)
|
|
{
|
|
InList.ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// StaticMeshDrawList elements shifting: specialization for an arrays
|
|
template<typename T, int32 N>
|
|
static void StaticMeshDrawListApplyWorldOffset(T(&InList)[N], FVector InOffset)
|
|
{
|
|
for (int32 i = 0; i < N; i++)
|
|
{
|
|
InList[i].ApplyWorldOffset(InOffset);
|
|
}
|
|
}
|
|
|
|
void FScene::ApplyWorldOffset_RenderThread(FVector InOffset)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_SceneApplyWorldOffset);
|
|
|
|
// Primitives
|
|
for (auto It = Primitives.CreateIterator(); It; ++It)
|
|
{
|
|
(*It)->ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// Precomputed light volumes
|
|
for (const FPrecomputedLightVolume* It : PrecomputedLightVolumes)
|
|
{
|
|
const_cast<FPrecomputedLightVolume*>(It)->ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// Precomputed visibility
|
|
if (PrecomputedVisibilityHandler)
|
|
{
|
|
const_cast<FPrecomputedVisibilityHandler*>(PrecomputedVisibilityHandler)->ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// Invalidate indirect lighting cache
|
|
IndirectLightingCache.SetLightingCacheDirty();
|
|
|
|
// Primitives octree
|
|
PrimitiveOctree.ApplyOffset(InOffset);
|
|
|
|
// Primitive bounds
|
|
for (auto It = PrimitiveBounds.CreateIterator(); It; ++It)
|
|
{
|
|
(*It).Origin+= InOffset;
|
|
}
|
|
|
|
// Primitive occlusion bounds
|
|
for (auto It = PrimitiveOcclusionBounds.CreateIterator(); It; ++It)
|
|
{
|
|
(*It).Origin+= InOffset;
|
|
}
|
|
|
|
// Lights
|
|
VectorRegister OffsetReg = VectorLoadFloat3_W0(&InOffset);
|
|
for (auto It = Lights.CreateIterator(); It; ++It)
|
|
{
|
|
(*It).BoundingSphereVector = VectorAdd((*It).BoundingSphereVector, OffsetReg);
|
|
(*It).LightSceneInfo->Proxy->ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// Lights octree
|
|
LightOctree.ApplyOffset(InOffset);
|
|
|
|
// Cached preshadows
|
|
for (auto It = CachedPreshadows.CreateIterator(); It; ++It)
|
|
{
|
|
(*It)->PreShadowTranslation-= InOffset;
|
|
(*It)->ShadowBounds.Center+= InOffset;
|
|
}
|
|
|
|
// Decals
|
|
for (auto It = Decals.CreateIterator(); It; ++It)
|
|
{
|
|
(*It)->ComponentTrans.AddToTranslation(InOffset);
|
|
}
|
|
|
|
// Wind sources
|
|
for (auto It = WindSources.CreateIterator(); It; ++It)
|
|
{
|
|
(*It)->ApplyWorldOffset(InOffset);
|
|
}
|
|
|
|
// Reflection captures
|
|
for (auto It = ReflectionSceneData.RegisteredReflectionCaptures.CreateIterator(); It; ++It)
|
|
{
|
|
FMatrix NewTransform = (*It)->BoxTransform.Inverse().ConcatTranslation(InOffset);
|
|
(*It)->SetTransform(NewTransform);
|
|
}
|
|
|
|
// StaticMeshDrawLists
|
|
StaticMeshDrawListApplyWorldOffset(PositionOnlyDepthDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(DepthDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(MaskedDepthDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassNoLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassCachedVolumeIndirectLightingDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassCachedPointIndirectLightingDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassSimpleDynamicLightingDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassHighQualityLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassDistanceFieldShadowMapLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassLowQualityLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassSelfShadowedTranslucencyDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(HitProxyDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(HitProxyDrawList_OpaqueOnly, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(VelocityDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(WholeSceneShadowDepthDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingNoLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingLowQualityLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightCSMDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightLightmapDrawList, InOffset);
|
|
StaticMeshDrawListApplyWorldOffset(BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList, InOffset);
|
|
|
|
// Motion blur
|
|
MotionBlurInfoData.ApplyOffset(InOffset);
|
|
}
|
|
|
|
void FScene::OnLevelAddedToWorld(FName LevelAddedName)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FLevelAddedToWorld,
|
|
class FScene*, Scene, this,
|
|
FName, LevelName, LevelAddedName,
|
|
{
|
|
Scene->OnLevelAddedToWorld_RenderThread(LevelName);
|
|
});
|
|
}
|
|
|
|
void FScene::OnLevelAddedToWorld_RenderThread(FName InLevelName)
|
|
{
|
|
// Mark level primitives
|
|
for (auto It = Primitives.CreateIterator(); It; ++It)
|
|
{
|
|
FPrimitiveSceneProxy* Proxy = (*It)->Proxy;
|
|
if (Proxy->LevelName == InLevelName)
|
|
{
|
|
Proxy->bIsComponentLevelVisible = true;
|
|
if (Proxy->NeedsLevelAddedToWorldNotification())
|
|
{
|
|
Proxy->OnLevelAddedToWorld();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dummy NULL scene interface used by dedicated servers.
|
|
*/
|
|
class FNULLSceneInterface : public FSceneInterface
|
|
{
|
|
public:
|
|
FNULLSceneInterface(UWorld* InWorld, bool bCreateFXSystem )
|
|
: World( InWorld )
|
|
, FXSystem( NULL )
|
|
{
|
|
World->Scene = this;
|
|
|
|
if (bCreateFXSystem)
|
|
{
|
|
World->CreateFXSystem();
|
|
}
|
|
else
|
|
{
|
|
World->FXSystem = NULL;
|
|
SetFXSystem(NULL);
|
|
}
|
|
}
|
|
|
|
virtual void AddPrimitive(UPrimitiveComponent* Primitive) override {}
|
|
virtual void RemovePrimitive(UPrimitiveComponent* Primitive) override {}
|
|
virtual void ReleasePrimitive(UPrimitiveComponent* Primitive) override {}
|
|
|
|
/** Updates the transform of a primitive which has already been added to the scene. */
|
|
virtual void UpdatePrimitiveTransform(UPrimitiveComponent* Primitive) override {}
|
|
virtual void UpdatePrimitiveAttachment(UPrimitiveComponent* Primitive) override {}
|
|
|
|
virtual void AddLight(ULightComponent* Light) override {}
|
|
virtual void RemoveLight(ULightComponent* Light) override {}
|
|
virtual void AddInvisibleLight(ULightComponent* Light) override {}
|
|
virtual void SetSkyLight(FSkyLightSceneProxy* Light) override {}
|
|
virtual void DisableSkyLight(FSkyLightSceneProxy* Light) override {}
|
|
|
|
virtual void AddDecal(UDecalComponent*) override {}
|
|
virtual void RemoveDecal(UDecalComponent*) override {}
|
|
virtual void UpdateDecalTransform(UDecalComponent* Decal) override {}
|
|
|
|
/** Updates the transform of a light which has already been added to the scene. */
|
|
virtual void UpdateLightTransform(ULightComponent* Light) override {}
|
|
virtual void UpdateLightColorAndBrightness(ULightComponent* Light) override {}
|
|
|
|
virtual void AddExponentialHeightFog(class UExponentialHeightFogComponent* FogComponent) override {}
|
|
virtual void RemoveExponentialHeightFog(class UExponentialHeightFogComponent* FogComponent) override {}
|
|
virtual void AddAtmosphericFog(class UAtmosphericFogComponent* FogComponent) override {}
|
|
virtual void RemoveAtmosphericFog(class UAtmosphericFogComponent* FogComponent) override {}
|
|
virtual FAtmosphericFogSceneInfo* GetAtmosphericFogSceneInfo() override { return NULL; }
|
|
virtual void AddWindSource(class UWindDirectionalSourceComponent* WindComponent) override {}
|
|
virtual void RemoveWindSource(class UWindDirectionalSourceComponent* WindComponent) override {}
|
|
virtual const TArray<class FWindSourceSceneProxy*>& GetWindSources_RenderThread() const override
|
|
{
|
|
static TArray<class FWindSourceSceneProxy*> NullWindSources;
|
|
return NullWindSources;
|
|
}
|
|
virtual void GetWindParameters(const FVector& Position, FVector& OutDirection, float& OutSpeed, float& OutMinGustAmt, float& OutMaxGustAmt) const override { OutDirection = FVector(1.0f, 0.0f, 0.0f); OutSpeed = 0.0f; OutMinGustAmt = 0.0f; OutMaxGustAmt = 0.0f; }
|
|
virtual void GetDirectionalWindParameters(FVector& OutDirection, float& OutSpeed, float& OutMinGustAmt, float& OutMaxGustAmt) const override { OutDirection = FVector(1.0f, 0.0f, 0.0f); OutSpeed = 0.0f; OutMinGustAmt = 0.0f; OutMaxGustAmt = 0.0f; }
|
|
virtual void AddSpeedTreeWind(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh) override {}
|
|
virtual void RemoveSpeedTreeWind(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh) override {}
|
|
virtual void RemoveSpeedTreeWind_RenderThread(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh) override {}
|
|
virtual void UpdateSpeedTreeWind(double CurrentTime) override {}
|
|
virtual FUniformBufferRHIParamRef GetSpeedTreeUniformBuffer(const FVertexFactory* VertexFactory) override { return FUniformBufferRHIParamRef(); }
|
|
|
|
virtual void Release() override {}
|
|
|
|
/**
|
|
* Retrieves the lights interacting with the passed in primitive and adds them to the out array.
|
|
*
|
|
* @param Primitive Primitive to retrieve interacting lights for
|
|
* @param RelevantLights [out] Array of lights interacting with primitive
|
|
*/
|
|
virtual void GetRelevantLights( UPrimitiveComponent* Primitive, TArray<const ULightComponent*>* RelevantLights ) const override {}
|
|
|
|
/**
|
|
* @return true if hit proxies should be rendered in this scene.
|
|
*/
|
|
virtual bool RequiresHitProxies() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Accessors.
|
|
virtual class UWorld* GetWorld() const override
|
|
{
|
|
return World;
|
|
}
|
|
|
|
/**
|
|
* Return the scene to be used for rendering
|
|
*/
|
|
virtual class FScene* GetRenderScene() override
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Sets the FX system associated with the scene.
|
|
*/
|
|
virtual void SetFXSystem( class FFXSystemInterface* InFXSystem ) override
|
|
{
|
|
FXSystem = InFXSystem;
|
|
}
|
|
|
|
/**
|
|
* Get the FX system associated with the scene.
|
|
*/
|
|
virtual class FFXSystemInterface* GetFXSystem() override
|
|
{
|
|
return FXSystem;
|
|
}
|
|
|
|
virtual bool HasAnyLights() const override { return false; }
|
|
private:
|
|
UWorld* World;
|
|
class FFXSystemInterface* FXSystem;
|
|
};
|
|
|
|
FSceneInterface* FRendererModule::AllocateScene(UWorld* World, bool bInRequiresHitProxies, bool bCreateFXSystem, ERHIFeatureLevel::Type InFeatureLevel)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
// Create a full fledged scene if we have something to render.
|
|
if (GIsClient && FApp::CanEverRender() && !GUsingNullRHI)
|
|
{
|
|
FScene* NewScene = new FScene(World, bInRequiresHitProxies, GIsEditor && !World->IsGameWorld(), bCreateFXSystem, InFeatureLevel);
|
|
AllocatedScenes.Add(NewScene);
|
|
return NewScene;
|
|
}
|
|
// And fall back to a dummy/ NULL implementation for commandlets and dedicated server.
|
|
else
|
|
{
|
|
return new FNULLSceneInterface(World, bCreateFXSystem);
|
|
}
|
|
}
|
|
|
|
void FRendererModule::RemoveScene(FSceneInterface* Scene)
|
|
{
|
|
check(IsInGameThread());
|
|
AllocatedScenes.Remove(Scene);
|
|
}
|
|
|
|
void FRendererModule::UpdateStaticDrawListsForMaterials(const TArray<const FMaterial*>& Materials)
|
|
{
|
|
for (TSet<FSceneInterface*>::TConstIterator SceneIt(AllocatedScenes); SceneIt; ++SceneIt)
|
|
{
|
|
(*SceneIt)->UpdateStaticDrawListsForMaterials(Materials);
|
|
}
|
|
}
|
|
|
|
FSceneViewStateInterface* FRendererModule::AllocateViewState()
|
|
{
|
|
return new FSceneViewState();
|
|
}
|
|
|
|
/** Maps the no light-map case to the appropriate base pass draw list. */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FNoLightMapPolicy> >& FScene::GetBasePassDrawList<FNoLightMapPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassNoLightMapDrawList[DrawType];
|
|
}
|
|
|
|
/** Maps the directional light-map texture case to the appropriate base pass draw list. */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy< TLightMapPolicy<HQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TLightMapPolicy<HQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassHighQualityLightMapDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy< TDistanceFieldShadowsAndLightMapPolicy<HQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy<HQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassDistanceFieldShadowMapLightMapDrawList[DrawType];
|
|
}
|
|
|
|
/** Maps the simple light-map texture case to the appropriate base pass draw list. */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy< TLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetBasePassDrawList< TLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassLowQualityLightMapDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FSelfShadowedTranslucencyPolicy> >& FScene::GetBasePassDrawList<FSelfShadowedTranslucencyPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassSelfShadowedTranslucencyDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FSelfShadowedCachedPointIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FSelfShadowedCachedPointIndirectLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassSelfShadowedCachedPointIndirectTranslucencyDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FCachedVolumeIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FCachedVolumeIndirectLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassCachedVolumeIndirectLightingDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FCachedPointIndirectLightingPolicy> >& FScene::GetBasePassDrawList<FCachedPointIndirectLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassCachedPointIndirectLightingDrawList[DrawType];
|
|
}
|
|
|
|
/** */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassDrawingPolicy<FSimpleDynamicLightingPolicy> >& FScene::GetBasePassDrawList<FSimpleDynamicLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassSimpleDynamicLightingDrawList[DrawType];
|
|
}
|
|
|
|
|
|
/** Maps the no light-map case to the appropriate base pass draw list. */
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FNoLightMapPolicy> >& FScene::GetForwardShadingBasePassDrawList<FNoLightMapPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingNoLightMapDrawList[DrawType];
|
|
}
|
|
|
|
/** Maps the simple light-map texture case to the appropriate base pass draw list. */
|
|
template<>
|
|
TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetForwardShadingBasePassDrawList< TLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingLowQualityLightMapDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList< TBasePassForForwardShadingDrawingPolicy< TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP> > >& FScene::GetForwardShadingBasePassDrawList< TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP> >(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingDistanceFieldShadowMapLightMapDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHIndirectPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingDirectionalLightAndSHIndirectDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHDirectionalIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHDirectionalIndirectPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingDirectionalLightAndSHDirectionalIndirectDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FSimpleDirectionalLightAndSHDirectionalCSMIndirectPolicy> >& FScene::GetForwardShadingBasePassDrawList<FSimpleDirectionalLightAndSHDirectionalCSMIndirectPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingDirectionalLightAndSHDirectionalCSMIndirectDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingMovableDirectionalLightDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightCSMLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightCSMLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingMovableDirectionalLightCSMDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightWithLightmapLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightWithLightmapLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingMovableDirectionalLightLightmapDrawList[DrawType];
|
|
}
|
|
|
|
template<>
|
|
TStaticMeshDrawList<TBasePassForForwardShadingDrawingPolicy<FMovableDirectionalLightCSMWithLightmapLightingPolicy> >& FScene::GetForwardShadingBasePassDrawList<FMovableDirectionalLightCSMWithLightmapLightingPolicy>(EBasePassDrawListType DrawType)
|
|
{
|
|
return BasePassForForwardShadingMovableDirectionalLightCSMLightmapDrawList[DrawType];
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
MotionBlurInfoData
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
FMotionBlurInfoData::FMotionBlurInfoData()
|
|
: bShouldClearMotionBlurInfo(false)
|
|
, bWorldIsPaused(false)
|
|
{
|
|
}
|
|
|
|
void FMotionBlurInfoData::UpdatePrimitiveMotionBlur(FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
check(PrimitiveSceneInfo && IsInRenderingThread());
|
|
|
|
const FPrimitiveSceneProxy* Proxy = PrimitiveSceneInfo->Proxy;
|
|
FPrimitiveComponentId ComponentId = PrimitiveSceneInfo->PrimitiveComponentId;
|
|
|
|
if (Proxy != NULL && ComponentId.IsValid() && Proxy->IsMovable())
|
|
{
|
|
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(ComponentId);
|
|
|
|
if(MotionBlurInfo)
|
|
{
|
|
if(!MotionBlurInfo->GetPrimitiveSceneInfo())
|
|
{
|
|
MotionBlurInfo->SetPrimitiveSceneInfo(PrimitiveSceneInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// add to the end
|
|
MotionBlurInfo = &MotionBlurInfos.Add(ComponentId, FMotionBlurInfo(ComponentId, PrimitiveSceneInfo));
|
|
}
|
|
|
|
//request that this primitive scene info caches its transform at the end of the frame
|
|
MotionBlurInfo->SetKeepAndUpdateThisFrame();
|
|
}
|
|
}
|
|
|
|
void FMotionBlurInfoData::RemovePrimitiveMotionBlur(FPrimitiveSceneInfo* PrimitiveSceneInfo)
|
|
{
|
|
check(PrimitiveSceneInfo && IsInRenderingThread());
|
|
|
|
const FPrimitiveSceneProxy* Proxy = PrimitiveSceneInfo->Proxy;
|
|
|
|
if (Proxy != NULL && PrimitiveSceneInfo->PrimitiveComponentId.IsValid() && Proxy->IsMovable())
|
|
{
|
|
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
|
|
|
|
if(MotionBlurInfo)
|
|
{
|
|
// in case someone called SetKeepAndUpdateThisFrame() before
|
|
MotionBlurInfo->SetKeepAndUpdateThisFrame(false);
|
|
MotionBlurInfo->SetPrimitiveSceneInfo(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMotionBlurInfo::UpdateMotionBlurInfo()
|
|
{
|
|
if(MBPrimitiveSceneInfo && MBPrimitiveSceneInfo->Proxy)
|
|
{
|
|
// only if the proxy is still there
|
|
CurrentLocalToWorld = MBPrimitiveSceneInfo->Proxy->GetLocalToWorld();
|
|
}
|
|
|
|
bKeepAndUpdateThisFrame = false;
|
|
}
|
|
|
|
// Doxygen has trouble parsing these functions because the header declaring them is in Engine, not Renderer
|
|
#if !UE_BUILD_DOCS
|
|
|
|
void FMotionBlurInfoData::StartFrame(bool bInWorldIsPaused)
|
|
{
|
|
bWorldIsPaused = bInWorldIsPaused;
|
|
|
|
if(!bWorldIsPaused)
|
|
{
|
|
for (TMap<FPrimitiveComponentId, FMotionBlurInfo>::TIterator It(MotionBlurInfos); It; ++It)
|
|
{
|
|
FMotionBlurInfo& MotionBlurInfo = It.Value();
|
|
|
|
MotionBlurInfo.OnStartFrame();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMotionBlurInfoData::UpdateMotionBlurCache(FScene* InScene)
|
|
{
|
|
check(InScene && IsInRenderingThread());
|
|
|
|
if(bWorldIsPaused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (InScene->GetFeatureLevel() >= ERHIFeatureLevel::SM4)
|
|
{
|
|
if(bShouldClearMotionBlurInfo)
|
|
{
|
|
// Clear the motion blur information for this frame.
|
|
MotionBlurInfos.Empty();
|
|
bShouldClearMotionBlurInfo = false;
|
|
}
|
|
else
|
|
{
|
|
for (TMap<FPrimitiveComponentId, FMotionBlurInfo>::TIterator It(MotionBlurInfos); It; ++It)
|
|
{
|
|
FMotionBlurInfo& MotionBlurInfo = It.Value();
|
|
|
|
if (MotionBlurInfo.GetKeepAndUpdateThisFrame())
|
|
{
|
|
MotionBlurInfo.UpdateMotionBlurInfo();
|
|
}
|
|
else
|
|
{
|
|
It.RemoveCurrent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMotionBlurInfoData::SetClearMotionBlurInfo()
|
|
{
|
|
bShouldClearMotionBlurInfo = true;
|
|
}
|
|
|
|
void FMotionBlurInfoData::ApplyOffset(FVector InOffset)
|
|
{
|
|
for (auto It = MotionBlurInfos.CreateIterator(); It; ++It)
|
|
{
|
|
It.Value().ApplyOffset(InOffset);
|
|
}
|
|
}
|
|
|
|
FString FMotionBlurInfoData::GetDebugString() const
|
|
{
|
|
return FString::Printf(TEXT("Num=%d Clear=%d"), MotionBlurInfos.Num(), bShouldClearMotionBlurInfo);
|
|
}
|
|
|
|
const FMotionBlurInfo* FMotionBlurInfoData::FindMBInfoIndex(FPrimitiveComponentId ComponentId) const
|
|
{
|
|
return MotionBlurInfos.Find(ComponentId);
|
|
}
|
|
|
|
FMotionBlurInfo* FMotionBlurInfoData::FindMBInfoIndex(FPrimitiveComponentId ComponentId)
|
|
{
|
|
return MotionBlurInfos.Find(ComponentId);
|
|
}
|
|
|
|
bool FMotionBlurInfoData::GetPrimitiveMotionBlurInfo(const FPrimitiveSceneInfo* PrimitiveSceneInfo, FMatrix& OutPreviousLocalToWorld)
|
|
{
|
|
check(IsInParallelRenderingThread());
|
|
|
|
if (PrimitiveSceneInfo && PrimitiveSceneInfo->PrimitiveComponentId.IsValid())
|
|
{
|
|
FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
|
|
|
|
if(MotionBlurInfo)
|
|
{
|
|
OutPreviousLocalToWorld = MotionBlurInfo->GetPreviousLocalToWorld();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FMotionBlurInfoData::GetPrimitiveMotionBlurInfo(const FPrimitiveSceneInfo* PrimitiveSceneInfo, FMatrix& OutPreviousLocalToWorld) const
|
|
{
|
|
check(IsInParallelRenderingThread());
|
|
|
|
if (PrimitiveSceneInfo && PrimitiveSceneInfo->PrimitiveComponentId.IsValid())
|
|
{
|
|
const FMotionBlurInfo* MotionBlurInfo = FindMBInfoIndex(PrimitiveSceneInfo->PrimitiveComponentId);
|
|
|
|
if (MotionBlurInfo)
|
|
{
|
|
OutPreviousLocalToWorld = MotionBlurInfo->GetPreviousLocalToWorld();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif
|