bool FIndirectLightingCache::IndirectLightingAllowed(FScene* Scene, FSceneRenderer& Renderer) const
{
	bool bAnyViewAllowsIndirectLightingCache = false;
	if (IsIndirectLightingCacheAllowed(GetFeatureLevel()) && Scene->PrecomputedLightVolumes.Num() > 0)
	{
		for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
		{
			bAnyViewAllowsIndirectLightingCache |= (bool)Renderer.Views[ViewIndex].Family->EngineShowFlags.IndirectLightingCache;
		}
	}
	return bAnyViewAllowsIndirectLightingCache;
}
void FPrimitiveSceneInfo::AddToScene(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())
	{
		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();
	}

	// 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->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 FIndirectLightingCache::UpdateCache(FScene* Scene, FSceneRenderer& Renderer, bool bAllowUnbuiltPreview)
{
	if (IsIndirectLightingCacheAllowed())
	{
		bool bAnyViewAllowsIndirectLightingCache = false;

		for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
		{
			bAnyViewAllowsIndirectLightingCache |= Renderer.Views[ViewIndex].Family->EngineShowFlags.IndirectLightingCache;
		}

		if (bAnyViewAllowsIndirectLightingCache)
		{
			SCOPE_CYCLE_COUNTER(STAT_UpdateIndirectLightingCache);

			TMap<FIntVector, FBlockUpdateInfo> BlocksToUpdate;
			TArray<FIndirectLightingCacheAllocation*> TransitionsOverTimeToUpdate;

			if (bUpdateAllCacheEntries)
			{
				const uint32 PrimitiveCount = Scene->Primitives.Num();

				for (uint32 PrimitiveIndex = 0; PrimitiveIndex < PrimitiveCount; ++PrimitiveIndex)
				{
					FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];

					UpdateCachePrimitive(Scene, PrimitiveSceneInfo, false, true, BlocksToUpdate, TransitionsOverTimeToUpdate);
				}
			}

			// Go over the views and operate on any relevant visible primitives
			for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
			{
				FViewInfo& View = Renderer.Views[ViewIndex];

				if (!bUpdateAllCacheEntries)
				{
					for (FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap); BitIt; ++BitIt)
					{
						uint32 PrimitiveIndex = BitIt.GetIndex();
						FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
						const FPrimitiveViewRelevance& PrimitiveRelevance = View.PrimitiveViewRelevanceMap[PrimitiveIndex];

						UpdateCachePrimitive(Scene, PrimitiveSceneInfo, bAllowUnbuiltPreview, PrimitiveRelevance.bOpaqueRelevance, BlocksToUpdate, TransitionsOverTimeToUpdate);
					}
				}

				UpdateTranslucentVolumeCache(View, BlocksToUpdate, TransitionsOverTimeToUpdate);
			}

			UpdateBlocks(Scene, Renderer.Views.GetTypedData(), BlocksToUpdate);

			UpdateTransitionsOverTime(TransitionsOverTimeToUpdate, Renderer.ViewFamily.DeltaWorldTime);

			if (GCacheDrawLightingSamples || Renderer.ViewFamily.EngineShowFlags.VolumeLightingSamples || GCacheDrawDirectionalShadowing)
			{
				FViewElementPDI DebugPDI(Renderer.Views.GetTypedData(), NULL);

				for (int32 VolumeIndex = 0; VolumeIndex < Scene->PrecomputedLightVolumes.Num(); VolumeIndex++)
				{
					const FPrecomputedLightVolume* PrecomputedLightVolume = Scene->PrecomputedLightVolumes[VolumeIndex];

					PrecomputedLightVolume->DebugDrawSamples(&DebugPDI, GCacheDrawDirectionalShadowing != 0);
				}
			}
		}

		bUpdateAllCacheEntries = false;
	}
}