Пример #1
0
/**
 * Executes async DDC gets for chunks stored in the derived data cache.
 * @param Chunks - Chunks to retrieve.
 * @param FirstChunkToLoad - Index of the first chunk to retrieve.
 * @param OutHandles - Handles to the asynchronous DDC gets.
 */
static void BeginLoadDerivedChunks(TIndirectArray<FStreamedAudioChunk>& Chunks, int32 FirstChunkToLoad, TArray<uint32>& OutHandles)
{
	FDerivedDataCacheInterface& DDC = GetDerivedDataCacheRef();
	OutHandles.AddZeroed(Chunks.Num());
	for (int32 ChunkIndex = FirstChunkToLoad; ChunkIndex < Chunks.Num(); ++ChunkIndex)
	{
		const FStreamedAudioChunk& Chunk = Chunks[ChunkIndex];
		if (!Chunk.DerivedDataKey.IsEmpty())
		{
			OutHandles[ChunkIndex] = DDC.GetAsynchronous(*Chunk.DerivedDataKey);
		}
	}
}
bool FDesktopPlatformWindows::UpdateFileAssociations()
{
	TIndirectArray<FRegistryRootedKey> Keys;
	GetRequiredRegistrySettings(Keys);

	for (int32 Idx = 0; Idx < Keys.Num(); Idx++)
	{
		if (!Keys[Idx].Write(true))
		{
			return false;
		}
	}

	return true;
}
Пример #3
0
int8 ComputeLODForMeshes( const TIndirectArray<class FStaticMesh>& StaticMeshes, const FSceneView& View, const FVector4& Origin, float SphereRadius, int32 ForcedLODLevel, float ScreenSizeScale )
{
	int8 LODToRender = 0;

	// Handle forced LOD level first
	if(ForcedLODLevel >= 0)
	{
		int8 MaxLOD = 0;
		for(int32 MeshIndex = 0 ; MeshIndex < StaticMeshes.Num() ; ++MeshIndex)
		{
			const FStaticMesh&  Mesh = StaticMeshes[MeshIndex];
			MaxLOD = FMath::Max(MaxLOD, Mesh.LODIndex);
		}
		LODToRender = FMath::Clamp<int8>(ForcedLODLevel, 0, MaxLOD);
	}
	else if (View.Family->EngineShowFlags.LOD)
	{
		int32 MinLODFound = INT_MAX;
		bool bFoundLOD = false;
		int32 NumMeshes = StaticMeshes.Num();

		const float ScreenSize = ComputeBoundsScreenSize(Origin, SphereRadius, View);

		for(int32 MeshIndex = NumMeshes-1 ; MeshIndex >= 0 ; --MeshIndex)
		{
			const FStaticMesh& Mesh = StaticMeshes[MeshIndex];

			float MeshScreenSize = Mesh.ScreenSize * ScreenSizeScale;

			if(MeshScreenSize >= ScreenSize)
			{
				LODToRender = Mesh.LODIndex;
				bFoundLOD = true;
				break;
			}

			MinLODFound = FMath::Min<int32>(MinLODFound, Mesh.LODIndex);
		}

		// If no LOD was found matching the screen size, use the lowest in the array instead of LOD 0, to handle non-zero MinLOD
		if (!bFoundLOD)
		{
			LODToRender = MinLODFound;
		}
	}
	return LODToRender;
}
Пример #4
0
int8 ComputeLODForMeshes( const TIndirectArray<class FStaticMesh>& StaticMeshes, const FSceneView& View, const FVector4& Origin, float SphereRadius, int32 ForcedLODLevel, float ScreenSizeScale )
{
	int8 LODToRender = 0;

	// Handle forced LOD level first
	if(ForcedLODLevel >= 0)
	{
		int8 MaxLOD = 0;
		for(int32 MeshIndex = 0 ; MeshIndex < StaticMeshes.Num() ; ++MeshIndex)
		{
			const FStaticMesh&  Mesh = StaticMeshes[MeshIndex];
			MaxLOD = FMath::Max(MaxLOD, Mesh.LODIndex);
		}
		LODToRender = FMath::Clamp<int8>(ForcedLODLevel, 0, MaxLOD);
	}
	else
	{
		int32 NumMeshes = StaticMeshes.Num();

		const float ScreenSize = ComputeBoundsScreenSize(Origin, SphereRadius, View);

		for(int32 MeshIndex = NumMeshes-1 ; MeshIndex >= 0 ; --MeshIndex)
		{
			const FStaticMesh& Mesh = StaticMeshes[MeshIndex];

			if(View.Family->EngineShowFlags.LOD == 1)
			{
				float MeshScreenSize = Mesh.ScreenSize * ScreenSizeScale;

				if(MeshScreenSize >= ScreenSize)
				{
					LODToRender = Mesh.LODIndex;
					break;
				}
			}
		}
	}
	return LODToRender;
}
Пример #5
0
/**
 * Executes all pending shadow-map encoding requests.
 * @param	InWorld				World in which the textures exist
 * @param	bLightingSuccessful	Whether the lighting build was successful or not.	 
 */
void FShadowMap2D::EncodeTextures(UWorld* InWorld , bool bLightingSuccessful )
{
	if ( bLightingSuccessful )
	{
		GWarn->BeginSlowTask( NSLOCTEXT("ShadowMap2D", "BeginEncodingShadowMapsTask", "Encoding shadow-maps"), false );
		const int32 PackedLightAndShadowMapTextureSize = InWorld->GetWorldSettings()->PackedLightAndShadowMapTextureSize;

		// Reset the pending shadow-map size.
		PendingShadowMapSize = 0;

		Sort(PendingShadowMaps.GetData(), PendingShadowMaps.Num(), FCompareShadowMaps());

		// Allocate texture space for each light-map.
		TIndirectArray<FShadowMapPendingTexture> PendingTextures;

		for(int32 ShadowMapIndex = 0;ShadowMapIndex < PendingShadowMaps.Num();ShadowMapIndex++)
		{
			FShadowMapAllocation& Allocation = PendingShadowMaps[ShadowMapIndex];

			// Find an existing texture which the light-map can be stored in.
			FShadowMapPendingTexture* Texture = NULL;
			for(int32 TextureIndex = 0;TextureIndex < PendingTextures.Num();TextureIndex++)
			{
				FShadowMapPendingTexture& ExistingTexture = PendingTextures[TextureIndex];

				// See if the new one will fit in the existing texture
				if ( ExistingTexture.AddElement( Allocation ) )
				{
					Texture = &ExistingTexture;
					break;
				}
			}

			// If there is no appropriate texture, create a new one.
			if(!Texture)
			{
				int32 NewTextureSizeX = PackedLightAndShadowMapTextureSize;
				int32 NewTextureSizeY = PackedLightAndShadowMapTextureSize;

				if(Allocation.MappedRect.Width() > NewTextureSizeX || Allocation.MappedRect.Height() > NewTextureSizeY)
				{
					NewTextureSizeX = FMath::RoundUpToPowerOfTwo(Allocation.MappedRect.Width());
					NewTextureSizeY = FMath::RoundUpToPowerOfTwo(Allocation.MappedRect.Height());
				}

				// If there is no existing appropriate texture, create a new one.
				Texture				= ::new(PendingTextures) FShadowMapPendingTexture(NewTextureSizeX,NewTextureSizeY);
				Texture->Outer		= Allocation.TextureOuter;
				Texture->Bounds		= Allocation.Bounds;
				Texture->ShadowmapFlags = Allocation.ShadowmapFlags;
				verify( Texture->AddElement( Allocation, true ) );
			}
		}
		
		for(int32 TextureIndex = 0;TextureIndex < PendingTextures.Num();TextureIndex++)
		{
			if (bUpdateStatus && (TextureIndex % 20) == 0)
			{
				GWarn->UpdateProgress(TextureIndex, PendingTextures.Num());
			}

			FShadowMapPendingTexture& PendingTexture = PendingTextures[TextureIndex];
			PendingTexture.StartEncoding(InWorld);
		}

		PendingTextures.Empty();
		PendingShadowMaps.Empty();

		GWarn->EndSlowTask();
	}
	else
	{
		PendingShadowMaps.Empty();
	}
}
Пример #6
0
	/** Add a new statistic to the internal map (or update an existing one) from the supplied component */
	UPrimitiveStats* Add(UPrimitiveComponent* InPrimitiveComponent, EPrimitiveObjectSets InObjectSet)
	{
		// Objects in transient package or transient objects are not part of level.
		if( InPrimitiveComponent->GetOutermost() == GetTransientPackage() || InPrimitiveComponent->HasAnyFlags( RF_Transient ) )
		{
			return NULL;
		}

		// Owned by a default object? Not part of a level either.
		if(InPrimitiveComponent->GetOuter() && InPrimitiveComponent->GetOuter()->IsDefaultSubobject() )
		{
			return NULL;
		}

		UStaticMeshComponent*	StaticMeshComponent		= Cast<UStaticMeshComponent>(InPrimitiveComponent);
		UModelComponent*		ModelComponent			= Cast<UModelComponent>(InPrimitiveComponent);
		USkeletalMeshComponent*	SkeletalMeshComponent	= Cast<USkeletalMeshComponent>(InPrimitiveComponent);
		ULandscapeComponent*	LandscapeComponent		= Cast<ULandscapeComponent>(InPrimitiveComponent);
		UObject*				Resource				= NULL;
		AActor*					ActorOuter				= Cast<AActor>(InPrimitiveComponent->GetOuter());

		int32 VertexColorMem		= 0;
		int32 InstVertexColorMem	= 0;
		// Calculate number of direct and other lights relevant to this component.
		int32 LightsLMCount			= 0;
		int32 LightsOtherCount		= 0;
		bool bUsesOnlyUnlitMaterials = InPrimitiveComponent->UsesOnlyUnlitMaterials();

		// The static mesh is a static mesh component's resource.
		if( StaticMeshComponent )
		{
			UStaticMesh* Mesh = StaticMeshComponent->StaticMesh;
			Resource = Mesh;

			// Calculate vertex color memory on the actual mesh.
			if( Mesh && Mesh->RenderData )
			{
				// Accumulate memory for each LOD
				for( int32 LODIndex = 0; LODIndex < Mesh->RenderData->LODResources.Num(); ++LODIndex )
				{
					VertexColorMem += Mesh->RenderData->LODResources[LODIndex].ColorVertexBuffer.GetAllocatedSize();
				}
			}

			// Calculate instanced vertex color memory used on the component.
			for( int32 LODIndex = 0; LODIndex < StaticMeshComponent->LODData.Num(); ++LODIndex )
			{
				// Accumulate memory for each LOD
				const FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[ LODIndex ];
				if( LODInfo.OverrideVertexColors )
				{
					InstVertexColorMem += LODInfo.OverrideVertexColors->GetAllocatedSize();	
				}
			}
			// Calculate the number of lightmap and shadow map lights
			if( !bUsesOnlyUnlitMaterials )
			{
				if( StaticMeshComponent->LODData.Num() > 0 )
				{
					FStaticMeshComponentLODInfo& ComponentLODInfo = StaticMeshComponent->LODData[0];
					if( ComponentLODInfo.LightMap )
					{
						LightsLMCount = ComponentLODInfo.LightMap->LightGuids.Num();
					}
				}
			}
		}
		// A model component is its own resource.
		else if( ModelComponent )			
		{
			// Make sure model component is referenced by level.
			ULevel* Level = CastChecked<ULevel>(ModelComponent->GetOuter());
			if( Level->ModelComponents.Find( ModelComponent ) != INDEX_NONE )
			{
				Resource = ModelComponent->GetModel();

				// Calculate the number of lightmap and shadow map lights
				if( !bUsesOnlyUnlitMaterials )
				{
					const TIndirectArray<FModelElement> Elements = ModelComponent->GetElements();
					if( Elements.Num() > 0 )
					{
						if( Elements[0].LightMap )
						{
							LightsLMCount = Elements[0].LightMap->LightGuids.Num();
						}
					}
				}
			}
		}
		// The skeletal mesh of a skeletal mesh component is its resource.
		else if( SkeletalMeshComponent )
		{
			USkeletalMesh* Mesh = SkeletalMeshComponent->SkeletalMesh;
			Resource = Mesh;
			// Calculate vertex color usage for skeletal meshes
			if( Mesh )
			{
				FSkeletalMeshResource* SkelMeshResource = Mesh->GetResourceForRendering();
				for( int32 LODIndex = 0; LODIndex < SkelMeshResource->LODModels.Num(); ++LODIndex )
				{
					const FStaticLODModel& LODModel = SkelMeshResource->LODModels[ LODIndex ];
					VertexColorMem += LODModel.ColorVertexBuffer.GetVertexDataSize();
				}
			}
		}
		// The landscape of a landscape component is its resource.
		else if (LandscapeComponent)
		{
			Resource = LandscapeComponent->GetLandscapeProxy();
			if (LandscapeComponent->LightMap)
			{
				LightsLMCount = LandscapeComponent->LightMap->LightGuids.Num();
			}
		}

		UWorld* World = InPrimitiveComponent->GetWorld();
	//	check(World); // @todo: re-instate this check once the GWorld migration has completed
		/// If we should skip the actor. Skip if the actor has no outer or if we are only showing selected actors and the actor isn't selected
		const bool bShouldSkip = World == NULL || ActorOuter == NULL || (ActorOuter != NULL && InObjectSet == PrimitiveObjectSets_SelectedObjects && ActorOuter->IsSelected() == false );
		// Dont' care about components without a resource.
		if(	Resource 
			// Require actor association for selection and to disregard mesh emitter components. The exception being model components.
			&&	(!bShouldSkip || (ModelComponent && InObjectSet != PrimitiveObjectSets_SelectedObjects ) )
			// Only list primitives in visible levels
			&&	IsInVisibleLevel( InPrimitiveComponent, World ) 
			// Don't list pending kill components.
			&&	!InPrimitiveComponent->IsPendingKill() )
		{
			// Retrieve relevant lights.
			TArray<const ULightComponent*> RelevantLights;
			World->Scene->GetRelevantLights( InPrimitiveComponent, &RelevantLights );

			// Only look for relevant lights if we aren't unlit.
			if( !bUsesOnlyUnlitMaterials )
			{
				// Lightmap and shadow map lights are calculated above, per component type, infer the "other" light count here
				LightsOtherCount = RelevantLights.Num() >= LightsLMCount ? RelevantLights.Num() - LightsLMCount : 0;
			}

			// Figure out memory used by light and shadow maps and light/ shadow map resolution.
			int32 LightMapWidth			= 0;
			int32 LightMapHeight		= 0;
			InPrimitiveComponent->GetLightMapResolution( LightMapWidth, LightMapHeight );
			int32 LMSMResolution		= FMath::Sqrt( LightMapHeight * LightMapWidth );
			int32 LightMapData			= 0;
			int32 LegacyShadowMapData	= 0;
			InPrimitiveComponent->GetLightAndShadowMapMemoryUsage( LightMapData, LegacyShadowMapData );

			// Check whether we already have an entry for the associated static mesh.
			UPrimitiveStats** StatsEntryPtr = ResourceToStatsMap.Find( Resource );
			if( StatsEntryPtr )
			{
				check(*StatsEntryPtr);
				UPrimitiveStats* StatsEntry = *StatsEntryPtr;

				// We do. Update existing entry.
				StatsEntry->Count++;
				StatsEntry->Actors.AddUnique(ActorOuter);
				StatsEntry->RadiusMin		= FMath::Min( StatsEntry->RadiusMin, InPrimitiveComponent->Bounds.SphereRadius );
				StatsEntry->RadiusMax		= FMath::Max( StatsEntry->RadiusMax, InPrimitiveComponent->Bounds.SphereRadius );
				StatsEntry->RadiusAvg		+= InPrimitiveComponent->Bounds.SphereRadius;
				StatsEntry->LightsLM		+= LightsLMCount;
				StatsEntry->LightsOther		+= LightsOtherCount;
				StatsEntry->LightMapData	+= (float)LightMapData / 1024.0f;
				StatsEntry->LMSMResolution	+= LMSMResolution;
				StatsEntry->UpdateNames();

				if ( !ModelComponent && !LandscapeComponent )
				{
					// Count instanced sections
					StatsEntry->InstSections += StatsEntry->Sections;
					StatsEntry->InstTriangles += StatsEntry->Triangles;
				}

				// ... in the case of a model component (aka BSP).
				if( ModelComponent )
				{
					// If Count represents the Model itself, we do NOT want to increment it now.
					StatsEntry->Count--;

					for (const auto& Element : ModelComponent->GetElements())
					{
						StatsEntry->Triangles += Element.NumTriangles;
						StatsEntry->Sections++;
					}

					StatsEntry->InstSections = StatsEntry->Sections;
					StatsEntry->InstTriangles = StatsEntry->Triangles;
				}
				else if( StaticMeshComponent )
				{
					// This stat is used by multiple components so accumulate instanced vertex color memory.
					StatsEntry->InstVertexColorMem += (float)InstVertexColorMem / 1024.0f;
				}
				else if (LandscapeComponent)
				{
					// If Count represents the Landscape itself, we do NOT want to increment it now.
					StatsEntry->Count--;
				}
			}
			else
			{
				// We don't. Create new base entry.
				UPrimitiveStats* NewStatsEntry = NewObject<UPrimitiveStats>();
				NewStatsEntry->AddToRoot();
				NewStatsEntry->Object			= Resource;
				NewStatsEntry->Actors.AddUnique(ActorOuter);
				NewStatsEntry->Count			= 1;
				NewStatsEntry->Triangles		= 0;
				NewStatsEntry->InstTriangles	= 0;
				NewStatsEntry->ResourceSize		= (float)(FArchiveCountMem(Resource).GetNum() + Resource->GetResourceSize(EResourceSizeMode::Exclusive)) / 1024.0f;
				NewStatsEntry->Sections			= 0;
				NewStatsEntry->InstSections = 0;
				NewStatsEntry->RadiusMin		= InPrimitiveComponent->Bounds.SphereRadius;
				NewStatsEntry->RadiusAvg		= InPrimitiveComponent->Bounds.SphereRadius;
				NewStatsEntry->RadiusMax		= InPrimitiveComponent->Bounds.SphereRadius;
				NewStatsEntry->LightsLM			= LightsLMCount;
				NewStatsEntry->LightsOther		= (float)LightsOtherCount;
				NewStatsEntry->LightMapData		= (float)LightMapData / 1024.0f;
				NewStatsEntry->LMSMResolution	= LMSMResolution;
				NewStatsEntry->VertexColorMem	= (float)VertexColorMem / 1024.0f;
				NewStatsEntry->InstVertexColorMem = (float)InstVertexColorMem / 1024.0f;
				NewStatsEntry->UpdateNames();

				// Fix up triangle and section count...

				// ... in the case of a static mesh component.
				if( StaticMeshComponent )
				{
					UStaticMesh* StaticMesh = StaticMeshComponent->StaticMesh;
					if( StaticMesh && StaticMesh->RenderData )
					{
						for( int32 SectionIndex=0; SectionIndex<StaticMesh->RenderData->LODResources[0].Sections.Num(); SectionIndex++ )
						{
							const FStaticMeshSection& StaticMeshSection = StaticMesh->RenderData->LODResources[0].Sections[SectionIndex];
							NewStatsEntry->Triangles	+= StaticMeshSection.NumTriangles;
							NewStatsEntry->Sections++;
						}
					}
				}
				// ... in the case of a model component (aka BSP).
				else if( ModelComponent )
				{
					TIndirectArray<FModelElement> Elements = ModelComponent->GetElements();
					for( int32 ElementIndex=0; ElementIndex<Elements.Num(); ElementIndex++ )
					{
						const FModelElement& Element = Elements[ElementIndex];
						NewStatsEntry->Triangles += Element.NumTriangles;
						NewStatsEntry->Sections++;
					}

				}
				// ... in the case of skeletal mesh component.
				else if( SkeletalMeshComponent )
				{
					USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
					if( SkeletalMesh )
					{
						FSkeletalMeshResource* SkelMeshResource = SkeletalMesh->GetResourceForRendering();
						if (SkelMeshResource->LODModels.Num())
						{
							const FStaticLODModel& BaseLOD = SkelMeshResource->LODModels[0];
							for( int32 SectionIndex=0; SectionIndex<BaseLOD.Sections.Num(); SectionIndex++ )
							{
								const FSkelMeshSection& Section = BaseLOD.Sections[SectionIndex];
								NewStatsEntry->Triangles += Section.NumTriangles;
								NewStatsEntry->Sections++;
							}
						}
					}
				}
				else if (LandscapeComponent)
				{
					TSet<UTexture2D*> UniqueTextures;
					for (auto ItComponents = LandscapeComponent->GetLandscapeProxy()->LandscapeComponents.CreateConstIterator(); ItComponents; ++ItComponents)
					{
						const ULandscapeComponent* CurrentComponent = *ItComponents;

						// count triangles and sections in the landscape
						NewStatsEntry->Triangles += FMath::Square(CurrentComponent->ComponentSizeQuads) * 2;
						NewStatsEntry->Sections += FMath::Square(CurrentComponent->NumSubsections);

						// count resource usage of landscape
						bool bNotUnique = false;
						UniqueTextures.Add(CurrentComponent->HeightmapTexture, &bNotUnique);
						if (!bNotUnique)
						{
							NewStatsEntry->ResourceSize += CurrentComponent->HeightmapTexture->GetResourceSize(EResourceSizeMode::Exclusive);
						}
						if (CurrentComponent->XYOffsetmapTexture)
						{
							UniqueTextures.Add(CurrentComponent->XYOffsetmapTexture, &bNotUnique);
							if (!bNotUnique)
							{
								NewStatsEntry->ResourceSize += CurrentComponent->XYOffsetmapTexture->GetResourceSize(EResourceSizeMode::Exclusive);
							}
						}

						for (auto ItWeightmaps = CurrentComponent->WeightmapTextures.CreateConstIterator(); ItWeightmaps; ++ItWeightmaps)
						{
							UniqueTextures.Add((*ItWeightmaps), &bNotUnique);
							if (!bNotUnique)
							{
								NewStatsEntry->ResourceSize += (*ItWeightmaps)->GetResourceSize(EResourceSizeMode::Exclusive);
							}
						}
					}
				}

				NewStatsEntry->InstTriangles = NewStatsEntry->Triangles;
				NewStatsEntry->InstSections = NewStatsEntry->Sections;

				// Add to map.
				ResourceToStatsMap.Add( Resource, NewStatsEntry );

				return NewStatsEntry;
			}
		}

		return NULL;
	}
Пример #7
0
void FPaperAtlasGenerator::HandleAssetChangedEvent(UPaperSpriteAtlas* Atlas)
{
	Atlas->MaxWidth = FMath::Clamp(Atlas->MaxWidth, 16, 4096);
	Atlas->MaxHeight = FMath::Clamp(Atlas->MaxHeight, 16, 4096);

	// Find all sprites that reference the atlas and force them loaded
	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));

	TArray<FAssetData> AssetList;
	TMultiMap<FName, FString> TagsAndValues;
	TagsAndValues.Add(TEXT("AtlasGroupGUID"), Atlas->AtlasGUID.ToString(EGuidFormats::Digits));
	AssetRegistryModule.Get().GetAssetsByTagValues(TagsAndValues, AssetList);

	TIndirectArray<FSingleTexturePaperAtlas> AtlasGenerators;

	// Start off with one page
	new (AtlasGenerators) FSingleTexturePaperAtlas(Atlas->MaxWidth, Atlas->MaxHeight);

	for (const FAssetData& SpriteRef : AssetList)
	{
		if (UPaperSprite* Sprite = Cast<UPaperSprite>(SpriteRef.GetAsset()))
		{
			//@TODO: Use the tight bounds instead of the source bounds
			const FVector2D SpriteSizeFloat = Sprite->GetSourceSize();
			const FIntPoint SpriteSize(FMath::TruncToInt(SpriteSizeFloat.X), FMath::TruncToInt(SpriteSizeFloat.Y));

			if (Sprite->GetSourceTexture() == nullptr)
			{
				UE_LOG(LogPaper2DEditor, Error, TEXT("Sprite %s has no source texture and cannot be packed"), *(Sprite->GetPathName()));
				continue;
			}

			//@TODO: Take padding into account (push this into the generator)
			if ((SpriteSize.X > Atlas->MaxWidth) || (SpriteSize.Y >= Atlas->MaxHeight))
			{
				// This sprite cannot ever fit into an atlas page
				UE_LOG(LogPaper2DEditor, Error, TEXT("Sprite %s (%d x %d) can never fit into atlas %s (%d x %d) due to maximum page size restrictions"),
					*(Sprite->GetPathName()),
					SpriteSize.X,
					SpriteSize.Y,
					*(Atlas->GetPathName()),
					Atlas->MaxWidth,
					Atlas->MaxHeight);
			}
			else
			{
				const FAtlasedTextureSlot* Slot = nullptr;

				// Does it fit in any existing pages?
				for (auto& Generator : AtlasGenerators)
				{
					Slot = Generator.AddSprite(Sprite);

					if (Slot != nullptr)
					{
						break;
					}
				}

				if (Slot == nullptr)
				{
					// Doesn't fit in any current pages, make a new one
					FSingleTexturePaperAtlas* NewGenerator = new (AtlasGenerators) FSingleTexturePaperAtlas(Atlas->MaxWidth, Atlas->MaxHeight);
					Slot = NewGenerator->AddSprite(Sprite);
				}

				if (Slot != nullptr)
				{
					UE_LOG(LogPaper2DEditor, Warning, TEXT("Allocated %s to (%d,%d)"), *(Sprite->GetPathName()), Slot->X, Slot->Y);
				}
				else
				{
					// ERROR: Didn't fit into a brand new page, maybe padding was wrong
					UE_LOG(LogPaper2DEditor, Error, TEXT("Failed to allocate %s into a brand new page"), *(Sprite->GetPathName()));
				}
			}
		}
	}

	// Turn the generators back into textures
	Atlas->GeneratedTextures.Empty(AtlasGenerators.Num());
	for (int32 GeneratorIndex = 0; GeneratorIndex < AtlasGenerators.Num(); ++GeneratorIndex)
	{
		FSingleTexturePaperAtlas& AtlasPage = AtlasGenerators[GeneratorIndex];

		UTexture2D* Texture = NewNamedObject<UTexture2D>(Atlas, NAME_None, RF_Public);
		Atlas->GeneratedTextures.Add(Texture);

		AtlasPage.CopyToTexture(Texture);

		// Now update the baked data for all the sprites to point there
		for (auto& SpriteToSlot : AtlasPage.SpriteToSlotMap)
		{
			UPaperSprite* Sprite = SpriteToSlot.Key;
			Sprite->Modify();
			const FAtlasedTextureSlot* Slot = SpriteToSlot.Value;
			Sprite->BakedSourceTexture = Texture;
			Sprite->BakedSourceUV = FVector2D(Slot->X, Slot->Y);
			Sprite->RebuildRenderData();
		}
	}

	//@TODO: Adjust the atlas rebuild code so that it tries to preserve existing sprites (optimize for the 'just added one to the end' case, preventing dirtying lots of assets unnecessarily)
	//@TODO: invoke this code when a sprite has the atlas group property modified
}
Пример #8
0
void UStaticMesh::SerializeLegacySouceData(FArchive& Ar, const FBoxSphereBounds& LegacyBounds)
{
	int32 InternalVersion = 0;
	bool bHaveSourceData = false;
	TArray<FStaticMeshOptimizationSettings> OptimizationSettings;
	bool bHasBeenSimplified = false;

	Ar << InternalVersion;
	Ar << bHaveSourceData;
	if (bHaveSourceData)
	{
		FStaticMeshSourceModel& SrcModel = *new(SourceModels) FStaticMeshSourceModel();

		// Serialize in the old source render data.
		FLegacyStaticMeshRenderData TempRenderData;
		TempRenderData.Serialize(Ar, this, INDEX_NONE);

		// Convert it to a raw mesh.
		FRawMesh RawMesh;
		BuildRawMeshFromRenderData(RawMesh, SrcModel.BuildSettings, TempRenderData, *GetPathName());

		// Store the raw mesh as our source model.
		SrcModel.RawMeshBulkData->SaveRawMesh(RawMesh);
	}
	Ar << OptimizationSettings;
	Ar << bHasBeenSimplified;

	// Convert any old optimization settings to the source model's reduction settings.
	for (int32 i = 0; i < OptimizationSettings.Num(); ++i)
	{
		if (!SourceModels.IsValidIndex(i))
		{
			new(SourceModels) FStaticMeshSourceModel();
		}
		FStaticMeshSourceModel& SrcModel = SourceModels[i];

		FMeshReductionSettings& NewSettings = SrcModel.ReductionSettings;
		switch (OptimizationSettings[i].ReductionMethod)
		{
		case OT_NumOfTriangles:
			NewSettings.PercentTriangles = FMath::Clamp(OptimizationSettings[i].NumOfTrianglesPercentage / 100.0f, 0.0f, 1.0f);
			NewSettings.MaxDeviation = 0.0f;
			break;
		case OT_MaxDeviation:
			NewSettings.PercentTriangles = 1.0f;
			NewSettings.MaxDeviation = OptimizationSettings[i].MaxDeviationPercentage * LegacyBounds.SphereRadius;
			break;
		default:
			NewSettings.PercentTriangles = 1.0f;
			NewSettings.MaxDeviation = 0.0f;
			break;
		}
		NewSettings.WeldingThreshold = OptimizationSettings[i].WeldingThreshold;
		NewSettings.HardAngleThreshold = OptimizationSettings[i].NormalsThreshold;
		NewSettings.SilhouetteImportance = (EMeshFeatureImportance::Type)OptimizationSettings[i].SilhouetteImportance;
		NewSettings.TextureImportance = (EMeshFeatureImportance::Type)OptimizationSettings[i].TextureImportance;
		NewSettings.ShadingImportance = (EMeshFeatureImportance::Type)OptimizationSettings[i].ShadingImportance;
	}

	// Serialize in any legacy LOD models.
	TIndirectArray<FLegacyStaticMeshRenderData> LegacyLODModels;
	LegacyLODModels.Serialize(Ar, this);

	TArray<FLegacyStaticMeshLODInfo> LegacyLODInfo;
	Ar << LegacyLODInfo;

	for (int32 LODIndex = 0; LODIndex < LegacyLODModels.Num(); ++LODIndex)
	{
		if (!SourceModels.IsValidIndex(LODIndex))
		{
			new(SourceModels) FStaticMeshSourceModel();
		}
		FStaticMeshSourceModel& SrcModel = SourceModels[LODIndex];

		// Really we want to use LOD0 only if the mesh has /not/ been simplified.
		// Unfortunately some data seems to be corrupted in source meshes but not LOD0, so just use LOD0.
		//if (SrcModel.RawMeshBulkData->IsEmpty() && (!bHasBeenSimplified || LODIndex == 0))
		{
			// Store the raw mesh for this LOD.
			FRawMesh RawMesh;
			BuildRawMeshFromRenderData(RawMesh, SrcModel.BuildSettings, LegacyLODModels[LODIndex], *GetPathName());
			SrcModel.RawMeshBulkData->SaveRawMesh(RawMesh);
		}

		// And make sure to clear reduction settings for LOD0.
		if (LODIndex == 0)
		{
			FMeshReductionSettings DefaultSettings;
			SrcModel.ReductionSettings = DefaultSettings;
		}

		// Always use a hash as the guid for legacy models.
		SrcModel.RawMeshBulkData->UseHashAsGuid(this);

		// Setup the material map for this LOD model.
		if (LODIndex == 0)
		{
			for (int32 SectionIndex = 0; SectionIndex < LegacyLODModels[LODIndex].Elements.Num(); ++SectionIndex)
			{
				FMeshSectionInfo Info;
				Info.MaterialIndex = Materials.Add(LegacyLODModels[LODIndex].Elements[SectionIndex].Material);
				Info.bEnableCollision = LegacyLODModels[LODIndex].Elements[SectionIndex].EnableCollision;
				Info.bCastShadow = LegacyLODModels[LODIndex].Elements[SectionIndex].bEnableShadowCasting;
				SectionInfoMap.Set(LODIndex, SectionIndex, Info);
			}
		}
		else
		{
			for (int32 SectionIndex = 0; SectionIndex < LegacyLODModels[LODIndex].Elements.Num(); ++SectionIndex)
			{
				FMeshSectionInfo Info;
				Info.MaterialIndex = Materials.AddUnique(LegacyLODModels[LODIndex].Elements[SectionIndex].Material);
				Info.bEnableCollision = LegacyLODModels[LODIndex].Elements[SectionIndex].EnableCollision;
				Info.bCastShadow = LegacyLODModels[LODIndex].Elements[SectionIndex].bEnableShadowCasting;
				SectionInfoMap.Set(LODIndex, SectionIndex, Info);
			}
		}
	}
}
void FDesktopPlatformWindows::GetRequiredRegistrySettings(TIndirectArray<FRegistryRootedKey> &RootedKeys)
{
	// Get the path to VersionSelector.exe. If we're running from UnrealVersionSelector itself, try to stick with the current configuration.
	FString DefaultVersionSelectorName = FPlatformProcess::ExecutableName(false);
	if (!DefaultVersionSelectorName.StartsWith(TEXT("UnrealVersionSelector")))
	{
		DefaultVersionSelectorName = TEXT("UnrealVersionSelector.exe");
	}
	FString ExecutableFileName = FString(FPlatformProcess::BaseDir()) / DefaultVersionSelectorName;

	// Defer to UnrealVersionSelector.exe in a launcher installation if it's got the same version number or greater.
	FString InstallDir;
	if (FWindowsPlatformMisc::QueryRegKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\EpicGames\\Unreal Engine"), TEXT("INSTALLDIR"), InstallDir) && (InstallDir.Len() > 0))
	{
		FString NormalizedInstallDir = InstallDir;
		FPaths::NormalizeDirectoryName(NormalizedInstallDir);

		FString InstalledExecutableFileName = NormalizedInstallDir / FString("Launcher/Engine/Binaries/Win64/UnrealVersionSelector.exe");
		if(GetShellIntegrationVersion(InstalledExecutableFileName) == GetShellIntegrationVersion(ExecutableFileName))
		{
			ExecutableFileName = InstalledExecutableFileName;
		}
	}

	// Get the path to the executable
	FPaths::MakePlatformFilename(ExecutableFileName);
	FString QuotedExecutableFileName = FString(TEXT("\"")) + ExecutableFileName + FString(TEXT("\""));

	// HKCU\SOFTWARE\Classes\.uproject
	FRegistryRootedKey *UserRootExtensionKey = new FRegistryRootedKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Classes\\.uproject"));
	RootedKeys.Add(UserRootExtensionKey);

	// HKLM\SOFTWARE\Classes\.uproject
	FRegistryRootedKey *RootExtensionKey = new FRegistryRootedKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Classes\\.uproject"));
	RootExtensionKey->Key = new FRegistryKey();
	RootExtensionKey->Key->SetValue(TEXT(""), TEXT("Unreal.ProjectFile"));
	RootedKeys.Add(RootExtensionKey);

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile
	FRegistryRootedKey *RootFileTypeKey = new FRegistryRootedKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Classes\\Unreal.ProjectFile"));
	RootFileTypeKey->Key = new FRegistryKey();
	RootFileTypeKey->Key->SetValue(TEXT(""), TEXT("Unreal Engine Project File"));
	RootFileTypeKey->Key->FindOrAddKey(L"DefaultIcon")->SetValue(TEXT(""), QuotedExecutableFileName);
	RootedKeys.Add(RootFileTypeKey);

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile\shell
	FRegistryKey *ShellKey = RootFileTypeKey->Key->FindOrAddKey(TEXT("shell"));

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile\shell\open
	FRegistryKey *ShellOpenKey = ShellKey->FindOrAddKey(TEXT("open"));
	ShellOpenKey->SetValue(L"", L"Open");
	ShellOpenKey->FindOrAddKey(L"command")->SetValue(L"", QuotedExecutableFileName + FString(TEXT(" /editor \"%1\"")));

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile\shell\run
	FRegistryKey *ShellRunKey = ShellKey->FindOrAddKey(TEXT("run"));
	ShellRunKey->SetValue(TEXT(""), TEXT("Launch game"));
	ShellRunKey->SetValue(TEXT("Icon"), QuotedExecutableFileName);
	ShellRunKey->FindOrAddKey(L"command")->SetValue(TEXT(""), QuotedExecutableFileName + FString(TEXT(" /game \"%1\"")));

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile\shell\rungenproj
	FRegistryKey *ShellProjectKey = ShellKey->FindOrAddKey(TEXT("rungenproj"));
	ShellProjectKey->SetValue(L"", L"Generate Visual Studio project files");
	ShellProjectKey->SetValue(L"Icon", QuotedExecutableFileName);
	ShellProjectKey->FindOrAddKey(L"command")->SetValue(TEXT(""), QuotedExecutableFileName + FString(TEXT(" /projectfiles \"%1\"")));

	// HKLM\SOFTWARE\Classes\Unreal.ProjectFile\shell\switchversion
	FRegistryKey *ShellVersionKey = ShellKey->FindOrAddKey(TEXT("switchversion"));
	ShellVersionKey->SetValue(TEXT(""), TEXT("Switch Unreal Engine version..."));
	ShellVersionKey->SetValue(TEXT("Icon"), QuotedExecutableFileName);
	ShellVersionKey->FindOrAddKey(L"command")->SetValue(TEXT(""), QuotedExecutableFileName + FString(TEXT(" /switchversion \"%1\"")));

	// If the user has manually selected something other than our extension, we need to delete it. Explorer explicitly disables set access on that keys in that folder, but we can delete the whole thing.
	const TCHAR* UserChoicePath = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.uproject\\UserChoice");

	// Figure out whether we need to delete it. If it's already set to our own ProgId, leave it alone.
	bool bDeleteUserChoiceKey = true;
	HKEY hUserChoiceKey;
	if(RegOpenKeyEx(HKEY_CURRENT_USER, UserChoicePath, 0, KEY_READ, &hUserChoiceKey) == S_OK)
	{
		TCHAR ProgId[128];
		::DWORD ProgIdSize = sizeof(ProgId);
		::DWORD ProgIdType = 0;
		if(RegQueryValueEx(hUserChoiceKey, TEXT("Progid"), NULL, &ProgIdType, (BYTE*)ProgId, &ProgIdSize) == ERROR_SUCCESS && ProgIdType == REG_SZ)
		{
			bDeleteUserChoiceKey = (FCString::Strcmp(ProgId, TEXT("Unreal.ProjectFile")) != 0);
		}
		RegCloseKey(hUserChoiceKey);
	}
	if(bDeleteUserChoiceKey)
	{
		RootedKeys.Add(new FRegistryRootedKey(HKEY_CURRENT_USER, UserChoicePath));
	}
}
Пример #10
0
/**
 * Executes all pending shadow-map encoding requests.
 * @param	InWorld				World in which the textures exist
 * @param	bLightingSuccessful	Whether the lighting build was successful or not.
 */
void FShadowMap2D::EncodeTextures(UWorld* InWorld , bool bLightingSuccessful)
{
	if ( bLightingSuccessful )
	{
		GWarn->BeginSlowTask( NSLOCTEXT("ShadowMap2D", "BeginEncodingShadowMapsTask", "Encoding shadow-maps"), false );
		const int32 PackedLightAndShadowMapTextureSize = InWorld->GetWorldSettings()->PackedLightAndShadowMapTextureSize;

		// Reset the pending shadow-map size.
		PendingShadowMapSize = 0;

		Sort(PendingShadowMaps.GetData(), PendingShadowMaps.Num(), FCompareShadowMaps());

		// Allocate texture space for each shadow-map.
		TIndirectArray<FShadowMapPendingTexture> PendingTextures;

		for (FShadowMapAllocationGroup& PendingGroup : PendingShadowMaps)
		{
			if (!ensure(PendingGroup.Allocations.Num() >= 1))
			{
				continue;
			}

			int32 MaxWidth = 0;
			int32 MaxHeight = 0;
			for (auto& Allocation : PendingGroup.Allocations)
			{
				MaxWidth = FMath::Max(MaxWidth, Allocation->MappedRect.Width());
				MaxHeight = FMath::Max(MaxHeight, Allocation->MappedRect.Height());
			}

			FShadowMapPendingTexture* Texture = nullptr;

			// Find an existing texture which the shadow-map can be stored in.
			// Shadowmaps will always be 4-pixel aligned...
			for (FShadowMapPendingTexture& ExistingTexture : PendingTextures)
			{
				if (ExistingTexture.AddElement(PendingGroup))
				{
					Texture = &ExistingTexture;
					break;
				}
			}

			if (!Texture)
			{
				int32 NewTextureSizeX = PackedLightAndShadowMapTextureSize;
				int32 NewTextureSizeY = PackedLightAndShadowMapTextureSize;

				// Assumes identically-sized allocations, fit into the smallest square
				const int32 AllocationCountX = FMath::CeilToInt(FMath::Sqrt(FMath::DivideAndRoundUp(PendingGroup.Allocations.Num() * MaxHeight, MaxWidth)));
				const int32 AllocationCountY = FMath::DivideAndRoundUp(PendingGroup.Allocations.Num(), AllocationCountX);
				const int32 AllocationSizeX = AllocationCountX * MaxWidth;
				const int32 AllocationSizeY = AllocationCountY * MaxHeight;

				if (AllocationSizeX > NewTextureSizeX || AllocationSizeY > NewTextureSizeY)
				{
					NewTextureSizeX = FMath::RoundUpToPowerOfTwo(AllocationSizeX);
					NewTextureSizeY = FMath::RoundUpToPowerOfTwo(AllocationSizeY);
				}

				// If there is no existing appropriate texture, create a new one.
				Texture = new FShadowMapPendingTexture(NewTextureSizeX, NewTextureSizeY);
				PendingTextures.Add(Texture);
				Texture->Outer = PendingGroup.TextureOuter;
				Texture->Bounds = PendingGroup.Bounds;
				Texture->ShadowmapFlags = PendingGroup.ShadowmapFlags;
				verify(Texture->AddElement(PendingGroup));
			}

			// Give the texture ownership of the allocations
			for (auto& Allocation : PendingGroup.Allocations)
			{
				Texture->Allocations.Add(Allocation.Release());
			}
		}
		PendingShadowMaps.Empty();

		// Encode all the pending textures.
		for (int32 TextureIndex = 0; TextureIndex < PendingTextures.Num(); TextureIndex++)
		{
			if (bUpdateStatus && (TextureIndex % 20) == 0)
			{
				GWarn->UpdateProgress(TextureIndex, PendingTextures.Num());
			}

			FShadowMapPendingTexture& PendingTexture = PendingTextures[TextureIndex];
			PendingTexture.StartEncoding(InWorld);
		}
		PendingTextures.Empty();

		GWarn->EndSlowTask();
	}
	else
	{
		PendingShadowMaps.Empty();
	}
}
	virtual bool CompressImage(
		const FImage& InImage,
		const struct FTextureBuildSettings& BuildSettings,
		bool bImageHasAlphaChannel,
		FCompressedImage2D& OutCompressedImage
		) const override
	{
		bool bCompressionSucceeded = false;

		const int iWidthInBlocks	= ((InImage.SizeX + 3) & ~ 3) / 4;
		const int iHeightInBlocks	= ((InImage.SizeY + 3) & ~ 3) / 4;
		const int iOutputBytes		= iWidthInBlocks * iHeightInBlocks * 16;
		OutCompressedImage.RawData.AddUninitialized(iOutputBytes);

		// When we allow async tasks to execute we do so with 4 lines of the image per task
		// This isn't optimal for long thin textures, but works well with how ISPC works
		const int iScansPerTask = 4;
		const int iNumTasks = FMath::Max((InImage.SizeY / iScansPerTask) - 1, 0);
		const bool bUseTasks = true;

		EPixelFormat CompressedPixelFormat = PF_Unknown;
		if ( BuildSettings.TextureFormatName == GTextureFormatNameBC6H )
		{
			FImage Image;
			InImage.CopyTo(Image, ERawImageFormat::RGBA16F, false);

			bc6h_enc_settings settings;
			GetProfile_bc6h_basic(&settings);

			if ( bUseTasks )
			{
				class FIntelCompressWorker : public FNonAbandonableTask
				{
				public:
					FIntelCompressWorker(bc6h_enc_settings* pEncSettings, FImage* pInImage, FCompressedImage2D* pOutImage, int yStart, int yEnd)
						: mpEncSettings(pEncSettings)
						, mpInImage(pInImage)
						, mpOutImage(pOutImage)
						, mYStart(yStart)
						, mYEnd(yEnd)
					{
					}

					void DoWork()
					{
						IntelBC6HCompressScans(mpEncSettings, mpInImage, mpOutImage, mYStart, mYEnd);
					}

					FORCEINLINE TStatId GetStatId() const
					{
						RETURN_QUICK_DECLARE_CYCLE_STAT(FIntelCompressWorker, STATGROUP_ThreadPoolAsyncTasks);
					}

					bc6h_enc_settings*	mpEncSettings;
					FImage*				mpInImage;
					FCompressedImage2D*	mpOutImage;
					int					mYStart;
					int					mYEnd;
				};
				typedef FAsyncTask<FIntelCompressWorker> FIntelCompressTask;

				// One less task because we'll do the final + non multiple of 4 inside this task
				TIndirectArray<FIntelCompressTask> CompressionTasks;
				CompressionTasks.Reserve(iNumTasks);
				for ( int iTask=0; iTask < iNumTasks; ++iTask )
				{
					auto* AsyncTask = new(CompressionTasks) FIntelCompressTask(&settings, &Image, &OutCompressedImage, iTask * iScansPerTask, (iTask + 1) * iScansPerTask);
					AsyncTask->StartBackgroundTask();
				}

				IntelBC6HCompressScans(&settings, &Image, &OutCompressedImage, iScansPerTask * iNumTasks, InImage.SizeY);

				// Wait completion
				for (int32 TaskIndex = 0; TaskIndex < CompressionTasks.Num(); ++TaskIndex)
				{
					CompressionTasks[TaskIndex].EnsureCompletion();
				}
			}
			else
			{
				IntelBC6HCompressScans(&settings, &Image, &OutCompressedImage, 0, InImage.SizeY);
			}

			CompressedPixelFormat = PF_BC6H;
			bCompressionSucceeded = true;
		}
		else if ( BuildSettings.TextureFormatName == GTextureFormatNameBC7 )
		{
			FImage Image;
			InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.bSRGB);

			bc7_enc_settings settings;
			if ( bImageHasAlphaChannel )
			{
				GetProfile_alpha_basic(&settings);
			}
			else
			{
				GetProfile_basic(&settings);
			}

			if ( bUseTasks )
			{
				class FIntelCompressWorker : public FNonAbandonableTask
				{
				public:
					FIntelCompressWorker(bc7_enc_settings* pEncSettings, FImage* pInImage, FCompressedImage2D* pOutImage, int yStart, int yEnd)
						: mpEncSettings(pEncSettings)
						, mpInImage(pInImage)
						, mpOutImage(pOutImage)
						, mYStart(yStart)
						, mYEnd(yEnd)
					{
					}

					void DoWork()
					{
						IntelBC7CompressScans(mpEncSettings, mpInImage, mpOutImage, mYStart, mYEnd);
					}

					FORCEINLINE TStatId GetStatId() const
					{
						RETURN_QUICK_DECLARE_CYCLE_STAT(FIntelCompressWorker, STATGROUP_ThreadPoolAsyncTasks);
					}

					bc7_enc_settings*	mpEncSettings;
					FImage*				mpInImage;
					FCompressedImage2D*	mpOutImage;
					int					mYStart;
					int					mYEnd;
				};
				typedef FAsyncTask<FIntelCompressWorker> FIntelCompressTask;

				// One less task because we'll do the final + non multiple of 4 inside this task
				TIndirectArray<FIntelCompressTask> CompressionTasks;
				CompressionTasks.Reserve(iNumTasks);
				for ( int iTask=0; iTask < iNumTasks; ++iTask )
				{
					auto* AsyncTask = new(CompressionTasks) FIntelCompressTask(&settings, &Image, &OutCompressedImage, iTask * iScansPerTask, (iTask + 1) * iScansPerTask);
					AsyncTask->StartBackgroundTask();
				}

				IntelBC7CompressScans(&settings, &Image, &OutCompressedImage, iScansPerTask * iNumTasks, InImage.SizeY);

				// Wait completion
				for (int32 TaskIndex = 0; TaskIndex < CompressionTasks.Num(); ++TaskIndex)
				{
					CompressionTasks[TaskIndex].EnsureCompletion();
				}
			}
			else
			{
				IntelBC7CompressScans(&settings, &Image, &OutCompressedImage, 0, InImage.SizeY);
			}

			CompressedPixelFormat = PF_BC7;
			bCompressionSucceeded = true;
		}

		if ( bCompressionSucceeded )
		{
			OutCompressedImage.SizeX = FMath::Max(InImage.SizeX, 4);
			OutCompressedImage.SizeY = FMath::Max(InImage.SizeY, 4);
			OutCompressedImage.PixelFormat = CompressedPixelFormat;
		}
		return bCompressionSucceeded;
	}
FLODMask ComputeLODForMeshes( const TIndirectArray<class FStaticMesh>& StaticMeshes, const FSceneView& View, const FVector4& Origin, float SphereRadius, int32 ForcedLODLevel, float ScreenSizeScale )
{
	FLODMask LODToRender;

	// Handle forced LOD level first
	if(ForcedLODLevel >= 0)
	{
		// Note: starting at -1 which is the default LODIndex, for cases where LODIndex didn't get set
		int8 MaxLOD = -1;
		for(int32 MeshIndex = 0 ; MeshIndex < StaticMeshes.Num() ; ++MeshIndex)
		{
			const FStaticMesh&  Mesh = StaticMeshes[MeshIndex];
			MaxLOD = FMath::Max(MaxLOD, Mesh.LODIndex);
		}
		LODToRender.SetLOD(FMath::Clamp<int8>(ForcedLODLevel, 0, MaxLOD));
	}
	else if (View.Family->EngineShowFlags.LOD)
	{
		int32 NumMeshes = StaticMeshes.Num();

		if (NumMeshes && StaticMeshes[0].bDitheredLODTransition)
		{
			for (int32 SampleIndex = 0; SampleIndex < 2; SampleIndex++)
			{
				int32 MinLODFound = INT_MAX;
				bool bFoundLOD = false;
				const float ScreenSize = ComputeTemporalLODBoundsScreenSize(Origin, SphereRadius, View, SampleIndex);

				for(int32 MeshIndex = NumMeshes-1 ; MeshIndex >= 0 ; --MeshIndex)
				{
					const FStaticMesh& Mesh = StaticMeshes[MeshIndex];

					float MeshScreenSize = Mesh.ScreenSize * ScreenSizeScale;

					if(MeshScreenSize >= ScreenSize)
					{
						LODToRender.SetLODSample(Mesh.LODIndex, SampleIndex);
						bFoundLOD = true;
						break;
					}

					MinLODFound = FMath::Min<int32>(MinLODFound, Mesh.LODIndex);
				}
				// If no LOD was found matching the screen size, use the lowest in the array instead of LOD 0, to handle non-zero MinLOD
				if (!bFoundLOD)
				{
					LODToRender.SetLODSample(MinLODFound, SampleIndex);
				}
			}
		}
		else
		{
			int32 MinLODFound = INT_MAX;
			bool bFoundLOD = false;
			const float ScreenSize = ComputeBoundsScreenSize(Origin, SphereRadius, View);

			for(int32 MeshIndex = NumMeshes-1 ; MeshIndex >= 0 ; --MeshIndex)
			{
				const FStaticMesh& Mesh = StaticMeshes[MeshIndex];

				float MeshScreenSize = Mesh.ScreenSize * ScreenSizeScale;

				if(MeshScreenSize >= ScreenSize)
				{
					LODToRender.SetLOD(Mesh.LODIndex);
					bFoundLOD = true;
					break;
				}

				MinLODFound = FMath::Min<int32>(MinLODFound, Mesh.LODIndex);
			}
			// If no LOD was found matching the screen size, use the lowest in the array instead of LOD 0, to handle non-zero MinLOD
			if (!bFoundLOD)
			{
				LODToRender.SetLOD(MinLODFound);
			}
		}
	}
	return LODToRender;
}