Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PrimitiveSceneInfo.cpp
Lina Halper 93572b606c #HLOD :
- fixed overflow issue
- fixed big actor issue
- hide static mesh again because it causes GLEO if editor is opened
- LODParentPrimitive is now private - have setter/getter
- Made sure LODActor isn't placed manually
- Added proper map error
- Removed it from FGlobalComponentReregisterContext because I don't think we'll need it anymore.

[CL 2527688 by Lina Halper in Main branch]
2015-04-27 19:10:22 -04:00

500 lines
16 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PrimitiveSceneInfo.cpp: Primitive scene info implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "../../Engine/Classes/Components/SkeletalMeshComponent.h"
#include "ParticleDefinitions.h"
/** An implementation of FStaticPrimitiveDrawInterface that stores the drawn elements for the rendering thread to use. */
class FBatchingSPDI : public FStaticPrimitiveDrawInterface
{
public:
// Constructor.
FBatchingSPDI(FPrimitiveSceneInfo* InPrimitiveSceneInfo):
PrimitiveSceneInfo(InPrimitiveSceneInfo)
{}
// FStaticPrimitiveDrawInterface.
virtual void SetHitProxy(HHitProxy* HitProxy)
{
CurrentHitProxy = HitProxy;
if(HitProxy)
{
// Only use static scene primitive hit proxies in the editor.
if(GIsEditor)
{
// Keep a reference to the hit proxy from the FPrimitiveSceneInfo, to ensure it isn't deleted while the static mesh still
// uses its id.
PrimitiveSceneInfo->HitProxies.Add(HitProxy);
}
}
}
virtual void DrawMesh(
const FMeshBatch& Mesh,
float ScreenSize,
bool bShadowOnly
)
{
if (Mesh.GetNumPrimitives() > 0)
{
check(Mesh.VertexFactory);
check(Mesh.VertexFactory->IsInitialized());
#if DO_CHECK
Mesh.CheckUniformBuffers();
#endif
FStaticMesh* StaticMesh = new(PrimitiveSceneInfo->StaticMeshes) FStaticMesh(
PrimitiveSceneInfo,
Mesh,
ScreenSize,
bShadowOnly,
CurrentHitProxy ? CurrentHitProxy->Id : FHitProxyId()
);
}
}
private:
FPrimitiveSceneInfo* PrimitiveSceneInfo;
TRefCountPtr<HHitProxy> CurrentHitProxy;
};
void FPrimitiveSceneInfoCompact::Init(FPrimitiveSceneInfo* InPrimitiveSceneInfo)
{
PrimitiveSceneInfo = InPrimitiveSceneInfo;
Proxy = PrimitiveSceneInfo->Proxy;
Bounds = PrimitiveSceneInfo->Proxy->GetBounds();
MinDrawDistance = PrimitiveSceneInfo->Proxy->GetMinDrawDistance();
MaxDrawDistance = PrimitiveSceneInfo->Proxy->GetMaxDrawDistance();
VisibilityId = PrimitiveSceneInfo->Proxy->GetVisibilityId();
bHasViewDependentDPG = Proxy->HasViewDependentDPG();
bCastDynamicShadow = PrimitiveSceneInfo->Proxy->CastsDynamicShadow();
bAffectDynamicIndirectLighting = PrimitiveSceneInfo->Proxy->AffectsDynamicIndirectLighting();
LpvBiasMultiplier = PrimitiveSceneInfo->Proxy->GetLpvBiasMultiplier();
StaticDepthPriorityGroup = bHasViewDependentDPG ? 0 : Proxy->GetStaticDepthPriorityGroup();
}
FPrimitiveSceneInfo::FPrimitiveSceneInfo(UPrimitiveComponent* InComponent,FScene* InScene):
Proxy(InComponent->SceneProxy),
PrimitiveComponentId(InComponent->ComponentId),
ComponentLastRenderTime(&InComponent->LastRenderTime),
IndirectLightingCacheAllocation(NULL),
CachedReflectionCaptureProxy(NULL),
bNeedsCachedReflectionCaptureUpdate(true),
bVelocityIsSupressed(false),
DefaultDynamicHitProxy(NULL),
LightList(NULL),
LastRenderTime(-FLT_MAX),
LastVisibilityChangeTime(0.0f),
Scene(InScene),
PackedIndex(INDEX_NONE),
ComponentForDebuggingOnly(InComponent),
bNeedsStaticMeshUpdate(false)
{
check(ComponentForDebuggingOnly);
check(PrimitiveComponentId.IsValid());
check(Proxy);
UPrimitiveComponent* SearchParentComponent = Cast<UPrimitiveComponent>(InComponent->GetAttachmentRoot());
if (SearchParentComponent && SearchParentComponent != InComponent)
{
LightingAttachmentRoot = SearchParentComponent->ComponentId;
}
// Only create hit proxies in the Editor as that's where they are used.
if (GIsEditor)
{
// Create a dynamic hit proxy for the primitive.
DefaultDynamicHitProxy = Proxy->CreateHitProxies(InComponent,HitProxies);
if( DefaultDynamicHitProxy )
{
DefaultDynamicHitProxyId = DefaultDynamicHitProxy->Id;
}
}
// set LOD parent info if exists
UPrimitiveComponent* LODParent = InComponent->GetLODParentPrimitive();
if (LODParent)
{
LODParentComponentId = LODParent->ComponentId;
}
}
FPrimitiveSceneInfo::~FPrimitiveSceneInfo()
{
check(!OctreeId.IsValidId());
}
void FPrimitiveSceneInfo::AddStaticMeshes(FRHICommandListImmediate& RHICmdList)
{
// Cache the primitive's static mesh elements.
FBatchingSPDI BatchingSPDI(this);
BatchingSPDI.SetHitProxy(DefaultDynamicHitProxy);
Proxy->DrawStaticElements(&BatchingSPDI);
StaticMeshes.Shrink();
for(int32 MeshIndex = 0;MeshIndex < StaticMeshes.Num();MeshIndex++)
{
FStaticMesh& Mesh = StaticMeshes[MeshIndex];
// Add the static mesh to the scene's static mesh list.
FSparseArrayAllocationInfo SceneArrayAllocation = Scene->StaticMeshes.AddUninitialized();
Scene->StaticMeshes[SceneArrayAllocation.Index] = &Mesh;
Mesh.Id = SceneArrayAllocation.Index;
// By this point, the index buffer render resource must be initialized
// Add the static mesh to the appropriate draw lists.
Mesh.AddToDrawLists(RHICmdList, Scene);
}
}
void FPrimitiveSceneInfo::AddToScene(FRHICommandListImmediate& RHICmdList, bool bUpdateStaticDrawLists)
{
check(IsInRenderingThread());
// If we are attaching a primitive that should be statically lit but has unbuilt lighting,
// Allocate space in the indirect lighting cache so that it can be used for previewing indirect lighting
if (Proxy->HasStaticLighting()
&& Proxy->NeedsUnbuiltPreviewLighting()
&& IsIndirectLightingCacheAllowed(Scene->GetFeatureLevel()))
{
FIndirectLightingCacheAllocation* PrimitiveAllocation = Scene->IndirectLightingCache.FindPrimitiveAllocation(PrimitiveComponentId);
if (PrimitiveAllocation)
{
IndirectLightingCacheAllocation = PrimitiveAllocation;
PrimitiveAllocation->SetDirty();
}
else
{
PrimitiveAllocation = Scene->IndirectLightingCache.AllocatePrimitive(this, true);
PrimitiveAllocation->SetDirty();
IndirectLightingCacheAllocation = PrimitiveAllocation;
}
}
if (bUpdateStaticDrawLists)
{
AddStaticMeshes(RHICmdList);
}
// create potential storage for our compact info
FPrimitiveSceneInfoCompact LocalCompactPrimitiveSceneInfo;
FPrimitiveSceneInfoCompact* CompactPrimitiveSceneInfo = &LocalCompactPrimitiveSceneInfo;
// if we are being added directly to the Octree, initialize the temp compact scene info,
// and let the Octree make a copy of it
LocalCompactPrimitiveSceneInfo.Init(this);
// Add the primitive to the octree.
check(!OctreeId.IsValidId());
Scene->PrimitiveOctree.AddElement(LocalCompactPrimitiveSceneInfo);
check(OctreeId.IsValidId());
// Set bounds.
FPrimitiveBounds& PrimitiveBounds = Scene->PrimitiveBounds[PackedIndex];
FBoxSphereBounds BoxSphereBounds = Proxy->GetBounds();
PrimitiveBounds.Origin = BoxSphereBounds.Origin;
PrimitiveBounds.SphereRadius = BoxSphereBounds.SphereRadius;
PrimitiveBounds.BoxExtent = BoxSphereBounds.BoxExtent;
PrimitiveBounds.MinDrawDistanceSq = FMath::Square(Proxy->GetMinDrawDistance());
PrimitiveBounds.MaxDrawDistance = Proxy->GetMaxDrawDistance();
// Store precomputed visibility ID.
int32 VisibilityBitIndex = Proxy->GetVisibilityId();
FPrimitiveVisibilityId& VisibilityId = Scene->PrimitiveVisibilityIds[PackedIndex];
VisibilityId.ByteIndex = VisibilityBitIndex / 8;
VisibilityId.BitMask = (1 << (VisibilityBitIndex & 0x7));
// Store occlusion flags.
uint8 OcclusionFlags = EOcclusionFlags::None;
if (Proxy->CanBeOccluded())
{
OcclusionFlags |= EOcclusionFlags::CanBeOccluded;
}
if (Proxy->HasSubprimitiveOcclusionQueries())
{
OcclusionFlags |= EOcclusionFlags::HasSubprimitiveQueries;
}
if (Proxy->AllowApproximateOcclusion()
// Allow approximate occlusion if attached, even if the parent does not have bLightAttachmentsAsGroup enabled
|| LightingAttachmentRoot.IsValid())
{
OcclusionFlags |= EOcclusionFlags::AllowApproximateOcclusion;
}
if (VisibilityBitIndex >= 0)
{
OcclusionFlags |= EOcclusionFlags::HasPrecomputedVisibility;
}
Scene->PrimitiveOcclusionFlags[PackedIndex] = OcclusionFlags;
// Store occlusion bounds.
FBoxSphereBounds OcclusionBounds = BoxSphereBounds;
if (Proxy->HasCustomOcclusionBounds())
{
OcclusionBounds = Proxy->GetCustomOcclusionBounds();
}
OcclusionBounds.BoxExtent.X = OcclusionBounds.BoxExtent.X + OCCLUSION_SLOP;
OcclusionBounds.BoxExtent.Y = OcclusionBounds.BoxExtent.Y + OCCLUSION_SLOP;
OcclusionBounds.BoxExtent.Z = OcclusionBounds.BoxExtent.Z + OCCLUSION_SLOP;
OcclusionBounds.SphereRadius = OcclusionBounds.SphereRadius + OCCLUSION_SLOP;
Scene->PrimitiveOcclusionBounds[PackedIndex] = OcclusionBounds;
// Store the component.
Scene->PrimitiveComponentIds[PackedIndex] = PrimitiveComponentId;
bNeedsCachedReflectionCaptureUpdate = true;
{
FMemMark MemStackMark(FMemStack::Get());
// Find lights that affect the primitive in the light octree.
for (FSceneLightOctree::TConstElementBoxIterator<SceneRenderingAllocator> LightIt(Scene->LightOctree, Proxy->GetBounds().GetBox());
LightIt.HasPendingElements();
LightIt.Advance())
{
const FLightSceneInfoCompact& LightSceneInfoCompact = LightIt.GetCurrentElement();
if (LightSceneInfoCompact.AffectsPrimitive(*CompactPrimitiveSceneInfo))
{
FLightPrimitiveInteraction::Create(LightSceneInfoCompact.LightSceneInfo,this);
}
}
}
INC_MEMORY_STAT_BY(STAT_PrimitiveInfoMemory, sizeof(*this) + StaticMeshes.GetAllocatedSize() + Proxy->GetMemoryFootprint());
}
void FPrimitiveSceneInfo::RemoveStaticMeshes()
{
// Remove static meshes from the scene.
StaticMeshes.Empty();
}
void FPrimitiveSceneInfo::RemoveFromScene(bool bUpdateStaticDrawLists)
{
check(IsInRenderingThread());
// implicit linked list. The destruction will update this "head" pointer to the next item in the list.
while(LightList)
{
FLightPrimitiveInteraction::Destroy(LightList);
}
// Remove the primitive from the octree.
check(OctreeId.IsValidId());
check(Scene->PrimitiveOctree.GetElementById(OctreeId).PrimitiveSceneInfo == this);
Scene->PrimitiveOctree.RemoveElement(OctreeId);
OctreeId = FOctreeElementId();
IndirectLightingCacheAllocation = NULL;
DEC_MEMORY_STAT_BY(STAT_PrimitiveInfoMemory, sizeof(*this) + StaticMeshes.GetAllocatedSize() + Proxy->GetMemoryFootprint());
if (bUpdateStaticDrawLists)
{
RemoveStaticMeshes();
}
}
void FPrimitiveSceneInfo::UpdateStaticMeshes(FRHICommandListImmediate& RHICmdList)
{
checkSlow(bNeedsStaticMeshUpdate);
bNeedsStaticMeshUpdate = false;
QUICK_SCOPE_CYCLE_COUNTER(STAT_FPrimitiveSceneInfo_UpdateStaticMeshes);
// Remove the primitive's static meshes from the draw lists they're currently in, and re-add them to the appropriate draw lists.
for (int32 MeshIndex = 0; MeshIndex < StaticMeshes.Num(); MeshIndex++)
{
StaticMeshes[MeshIndex].RemoveFromDrawLists();
StaticMeshes[MeshIndex].AddToDrawLists(RHICmdList, Scene);
}
}
void FPrimitiveSceneInfo::BeginDeferredUpdateStaticMeshes()
{
// Set a flag which causes InitViews to update the static meshes the next time the primitive is visible.
bNeedsStaticMeshUpdate = true;
}
void FPrimitiveSceneInfo::LinkLODParentComponent()
{
if (LODParentComponentId.IsValid())
{
Scene->SceneLODHierarchy.AddChildNode(LODParentComponentId, this);
}
}
void FPrimitiveSceneInfo::UnlinkLODParentComponent()
{
if(LODParentComponentId.IsValid())
{
Scene->SceneLODHierarchy.RemoveChildNode(LODParentComponentId, this);
// I don't think this will be reused but just in case
LODParentComponentId = FPrimitiveComponentId();
}
}
void FPrimitiveSceneInfo::LinkAttachmentGroup()
{
// Add the primitive to its attachment group.
if (LightingAttachmentRoot.IsValid())
{
FAttachmentGroupSceneInfo* AttachmentGroup = Scene->AttachmentGroups.Find(LightingAttachmentRoot);
if (!AttachmentGroup)
{
// If this is the first primitive attached that uses this attachment parent, create a new attachment group.
AttachmentGroup = &Scene->AttachmentGroups.Add(LightingAttachmentRoot, FAttachmentGroupSceneInfo());
}
AttachmentGroup->Primitives.Add(this);
}
else if (Proxy->LightAttachmentsAsGroup())
{
FAttachmentGroupSceneInfo* AttachmentGroup = Scene->AttachmentGroups.Find(PrimitiveComponentId);
if (!AttachmentGroup)
{
// Create an empty attachment group
AttachmentGroup = &Scene->AttachmentGroups.Add(PrimitiveComponentId, FAttachmentGroupSceneInfo());
}
AttachmentGroup->ParentSceneInfo = this;
}
}
void FPrimitiveSceneInfo::UnlinkAttachmentGroup()
{
// Remove the primitive from its attachment group.
if (LightingAttachmentRoot.IsValid())
{
FAttachmentGroupSceneInfo& AttachmentGroup = Scene->AttachmentGroups.FindChecked(LightingAttachmentRoot);
AttachmentGroup.Primitives.RemoveSwap(this);
if (AttachmentGroup.Primitives.Num() == 0)
{
// If this was the last primitive attached that uses this attachment root, free the group.
Scene->AttachmentGroups.Remove(LightingAttachmentRoot);
}
}
else if (Proxy->LightAttachmentsAsGroup())
{
FAttachmentGroupSceneInfo* AttachmentGroup = Scene->AttachmentGroups.Find(PrimitiveComponentId);
if (AttachmentGroup)
{
AttachmentGroup->ParentSceneInfo = NULL;
}
}
}
void FPrimitiveSceneInfo::GatherLightingAttachmentGroupPrimitives(TArray<FPrimitiveSceneInfo*, SceneRenderingAllocator>& OutChildSceneInfos)
{
OutChildSceneInfos.Add(this);
if (!LightingAttachmentRoot.IsValid() && Proxy->LightAttachmentsAsGroup())
{
const FAttachmentGroupSceneInfo* AttachmentGroup = Scene->AttachmentGroups.Find(PrimitiveComponentId);
if (AttachmentGroup)
{
for (int32 ChildIndex = 0; ChildIndex < AttachmentGroup->Primitives.Num(); ChildIndex++)
{
FPrimitiveSceneInfo* ShadowChild = AttachmentGroup->Primitives[ChildIndex];
checkSlow(!OutChildSceneInfos.Contains(ShadowChild))
OutChildSceneInfos.Add(ShadowChild);
}
}
}
}
FBoxSphereBounds FPrimitiveSceneInfo::GetAttachmentGroupBounds() const
{
FBoxSphereBounds Bounds = Proxy->GetBounds();
if (!LightingAttachmentRoot.IsValid() && Proxy->LightAttachmentsAsGroup())
{
const FAttachmentGroupSceneInfo* AttachmentGroup = Scene->AttachmentGroups.Find(PrimitiveComponentId);
if (AttachmentGroup)
{
for (int32 ChildIndex = 0; ChildIndex < AttachmentGroup->Primitives.Num(); ChildIndex++)
{
FPrimitiveSceneInfo* AttachmentChild = AttachmentGroup->Primitives[ChildIndex];
Bounds = Bounds + AttachmentChild->Proxy->GetBounds();
}
}
}
return Bounds;
}
uint32 FPrimitiveSceneInfo::GetMemoryFootprint()
{
return( sizeof( *this ) + HitProxies.GetAllocatedSize() + StaticMeshes.GetAllocatedSize() );
}
bool FPrimitiveSceneInfo::ShouldRenderVelocity(const FViewInfo& View, bool bCheckVisibility) const
{
int32 PrimitiveId = GetIndex();
if (bCheckVisibility)
{
const bool bVisible = View.PrimitiveVisibilityMap[PrimitiveId];
// Only render if visible.
if (!bVisible)
{
return false;
}
}
const FPrimitiveViewRelevance& PrimitiveViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];
if (!Proxy->IsMovable())
{
return false;
}
// !Skip translucent objects as they don't support velocities and in the case of particles have a significant CPU overhead.
if (!PrimitiveViewRelevance.bOpaqueRelevance || !PrimitiveViewRelevance.bRenderInMainPass)
{
return false;
}
const float LODFactorDistanceSquared = (Proxy->GetBounds().Origin - View.ViewMatrices.ViewOrigin).SizeSquared() * FMath::Square(View.LODDistanceFactor);
// The minimum projected screen radius for a primitive to be drawn in the velocity pass, as a fraction of half the horizontal screen width (likely to be 0.08f)
float MinScreenRadiusForVelocityPass = View.FinalPostProcessSettings.MotionBlurPerObjectSize * (2.0f / 100.0f);
float MinScreenRadiusForVelocityPassSquared = FMath::Square(MinScreenRadiusForVelocityPass);
// Skip primitives that only cover a small amount of screenspace, motion blur on them won't be noticeable.
if (FMath::Square(Proxy->GetBounds().SphereRadius) <= MinScreenRadiusForVelocityPassSquared * LODFactorDistanceSquared)
{
return false;
}
// Only render primitives with velocity.
if (!FVelocityDrawingPolicy::HasVelocity(View, this))
{
return false;
}
return true;
}
void FPrimitiveSceneInfo::ApplyWorldOffset(FVector InOffset)
{
Proxy->ApplyWorldOffset(InOffset);
}