Пример #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);
		}
	}
}
Пример #2
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;
}
Пример #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
	{
		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;
}
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;
}
Пример #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);
			}
		}
	}
}
Пример #9
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;
}