Files
UnrealEngine/Engine/Source/Editor/VirtualTexturingEditor/Private/VirtualTextureConversionWorker.cpp
2025-05-18 13:04:45 +08:00

727 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VirtualTextureConversionWorker.h"
#include "Engine/Texture2D.h"
#include "Misc/PackageName.h"
#include "Materials/Material.h"
#include "Materials/MaterialAttributeDefinitionMap.h"
#include "Materials/MaterialInstance.h"
#include "Materials/MaterialFunction.h"
#include "Materials/MaterialFunctionInstance.h"
#include "Materials/MaterialFunctionInterface.h"
#include "Materials/MaterialExpressionTextureSampleParameter.h"
#include "MaterialShared.h"
#include "MaterialEditor/PreviewMaterial.h"
#include "Factories/MaterialFactoryNew.h"
#include "EditorFramework/AssetImportData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "ObjectTools.h"
#include "Editor.h"
#include "Misc/ScopedSlowTask.h"
#include "EditorSupportDelegates.h"
#include "MaterialGraph/MaterialGraph.h"
#include "MaterialEditingLibrary.h"
#include "AssetRegistry/AssetRegistryHelpers.h"
#include "UObject/UObjectIterator.h"
#define LOCTEXT_NAMESPACE "AssetVTConversion"
DEFINE_LOG_CATEGORY_STATIC(LogVirtualTextureConversion, Log, All);
namespace VTConversionWorkerUtil
{
template <class T> void GetReferencersOfType(UObject *Object, TArray<T*> &OutObjects)
{
TArray<FAssetData> OutAssetDatas;
UAssetRegistryHelpers::FindReferencersOfAssetOfClass(Object, { T::StaticClass() }, OutAssetDatas);
FScopedSlowTask SlowTask(static_cast<float>(OutAssetDatas.Num()), LOCTEXT("ConvertToVT_Progress_LoadingObjects", "Loading Objects..."));
for (auto Data : OutAssetDatas)
{
SlowTask.EnterProgressFrame();
if (Data.IsInstanceOf(T::StaticClass()))
{
T *IsOk = Cast<T>(Data.GetAsset());
if (IsOk != nullptr)
{
OutObjects.Add(IsOk);
}
}
}
}
}
static void GetPreviewMaterials(UTexture2D* InTexture, TArray<UMaterialInterface*>& OutMaterials)
{
for (TObjectIterator<UPreviewMaterial> It; It; ++It)
{
UMaterial* Material = *It;
for (uint32 PropertyIndex = 0u; PropertyIndex < MP_MAX; ++PropertyIndex)
{
const EMaterialProperty PropertyToValidate = (EMaterialProperty)PropertyIndex;
if (Material->IsTextureReferencedByProperty(PropertyToValidate, InTexture))
{
OutMaterials.Add(Material);
break;
}
}
}
}
bool IsVirtualTextureValidForMaterial(UMaterialInterface* InMaterial, UTexture2D* InTexture)
{
for (uint32 PropertyIndex = 0u; PropertyIndex < MP_MAX; ++PropertyIndex)
{
const EMaterialProperty PropertyToValidate = (EMaterialProperty)PropertyIndex;
const EShaderFrequency ShaderFrequencyToValidate = FMaterialAttributeDefinitionMap::GetShaderFrequency(PropertyToValidate);
if (PropertyToValidate == MP_OpacityMask || ShaderFrequencyToValidate != SF_Pixel)
{
// see if the texture is referenced by a property that doesn't support virtual texture access
if (InMaterial->IsTextureReferencedByProperty(PropertyToValidate, InTexture))
{
return false;
}
}
}
return true;
}
void FVirtualTextureConversionWorker::AddReferencedObjects( FReferenceCollector& Collector )
{
Collector.AddReferencedObjects( UserTextures );
Collector.AddReferencedObjects( Textures );
Collector.AddReferencedObjects( Materials );
Collector.AddReferencedObjects( Functions );
Collector.AddReferencedObjects( SizeRejectedTextures );
Collector.AddReferencedObjects( MaterialRejectedTextures );
for (auto& KV : AuditTrail)
{
Collector.AddReferencedObject(KV.Key);
}
}
void FVirtualTextureConversionWorker::FindAllTexturesAndMaterials_Iteration(TSet<UMaterial*>& InAffectedMaterials,
TSet<UMaterialFunctionInterface*>& InAffectedFunctions,
TSet<UTexture2D*>& InAffectedTextures,
TSet<TObjectPtr<UTexture2D>>& InInvalidTextures,
FScopedSlowTask& Task)
{
TArray<UMaterialInterface *>MaterialInferfaces; // All parents and instances
TArray<UMaterialInterface *>MaterialHeap; // Temporary work heap
TArray<UMaterialFunctionInterface *>FunctionInferfaces; // All parents and instances
TArray<UMaterialFunctionInterface *>FunctionHeap; // Temporary work heap
TMap<UMaterial*, TSet<FMaterialParameterInfo>> ParametersToVtIze;
TMap<UMaterialFunctionInterface*, TSet<FMaterialParameterInfo>> FunctionParametersToVtIze;
// Find all materials that reference the passed in textures
// This will also load these materials.
TArray<UMaterialInterface *> MaterialsUsedByAffectedTextures;
for (TSet<UTexture2D*>::TIterator TextureIter(InAffectedTextures); TextureIter; ++TextureIter)
{
UTexture2D* Tex2D = *TextureIter;
Task.EnterProgressFrame();
TArray<UMaterialInterface *> MaterialsUsingTexture;
VTConversionWorkerUtil::GetReferencersOfType(Tex2D, MaterialsUsingTexture);
/*for (auto Material : MaterialsUsingTexture)
{
AuditTrail.Add(Material, FAuditTrail(
Tex2D,
FString::Printf(TEXT("references texture"))
));
}*/
// Check all materials using texture, make sure we're able to convert this texture
bool bIsVirtualTextureValid = true;
for (UMaterialInterface* Material : MaterialsUsingTexture)
{
if (!bConvertBackward && !IsVirtualTextureValidForMaterial(Material, Tex2D))
{
AuditTrail.Add(Tex2D, FAuditTrail(
Material,
TEXT("does not support VT usage on material")));
bIsVirtualTextureValid = false;
break;
}
}
if (bIsVirtualTextureValid)
{
// If this is an engine texture, we'll make a copy and update any needed references to point to the new copy
// since we're not changing the original texture, we don't need to bring in any additional dependencies
// Non-power-2 textures won't convert to VT, don't bring any dependencies for them
if (!Tex2D->GetPathName().StartsWith("/Engine/") &&
(Tex2D->Source.AreAllBlocksPowerOfTwo() || Tex2D->PowerOfTwoMode != ETexturePowerOfTwoSetting::None))
{
// Also get any preview materials that reference the given texture
// We need to convert these to ensure any active material editors remain valid
GetPreviewMaterials(Tex2D, MaterialsUsedByAffectedTextures);
MaterialsUsedByAffectedTextures.Append(MaterialsUsingTexture);
}
}
else
{
TextureIter.RemoveCurrent();
InInvalidTextures.Add(Tex2D);
}
}
// Find all materials that reference the passed in functions
// This will also load these materials.
for (UMaterialFunctionInterface* Func : InAffectedFunctions)
{
Task.EnterProgressFrame();
TArray<UMaterialInterface *> MaterialsUsingFunction;
VTConversionWorkerUtil::GetReferencersOfType(Func, MaterialsUsingFunction);
/*for (auto Material : MaterialsUsingFunction)
{
AuditTrail.Add(Material, FAuditTrail(
Func,
FString::Printf(TEXT("references function"))
));
}*/
MaterialsUsedByAffectedTextures.Append(MaterialsUsingFunction);
}
// Find all the root materials of the found instances and add them to our
// working lists.
for (UMaterialInterface *If : MaterialsUsedByAffectedTextures)
{
// It's a material?
UMaterial *Material = Cast<UMaterial>(If);
// It's a material instance? we're only interested in it's root for now
UMaterialInstance *MaterialInstance = Cast<UMaterialInstance>(If);
if (MaterialInstance != nullptr)
{
Material = MaterialInstance->GetMaterial();
}
check(Material); // It's something else entirely??
MaterialInferfaces.AddUnique(Material);
MaterialHeap.AddUnique(Material);
bool bAlreadyExists;
InAffectedMaterials.Add(Material, &bAlreadyExists);
if (!bAlreadyExists)
{
AuditTrail.Add(Material, FAuditTrail(
If,
FString::Printf(TEXT("is the child of"))
));
}
}
// We now have a set of "root" materials which will be affected by changing InTextures to VT.
// Now find all children of these materials which will also be affected trough parameters now requiring VT textures being set on them.
// This will again load any child instances and their dependencies which aren't loaded yet
TArray<UMaterialInterface *>VisitedMaterials;
while (MaterialHeap.Num() > 0)
{
UMaterialInterface *ParentMaterial = MaterialHeap[0];
MaterialHeap.RemoveAt(0);
if (VisitedMaterials.Contains(ParentMaterial))
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Circular inheritance chain!? %s"), *ParentMaterial->GetName());
continue;
}
VisitedMaterials.Add(ParentMaterial);
// Check all parameters of the current material. If they reference a texture
// we want to convert to VT flag the parameter (this will then cause all textures assigned to this parameter to convert to vt as well)
TArray<FMaterialParameterInfo> ParameterInfos;
TArray<FGuid> ParameterGuids;
ParentMaterial->GetAllTextureParameterInfo(ParameterInfos, ParameterGuids);
for (FMaterialParameterInfo& ParamInfo : ParameterInfos)
{
UTexture *ParamValue = nullptr;
if (ParentMaterial->GetTextureParameterValue(ParamInfo, ParamValue))
{
UTexture2D *ParamValue2D = Cast<UTexture2D>(ParamValue);
if (ParamValue2D != nullptr)
{
if (InAffectedTextures.Contains(ParamValue2D))
{
//UE_LOG(LogVirtualTextureConversion, Display, TEXT("Adding parameter %s because it references %s on %s >> %s"), *ParamInfo.Name.ToString(), *ParamValue2D->GetPathName(), *ParentMaterial->GetMaterial()->GetPathName(), *ParentMaterial->GetPathName());
ParametersToVtIze.FindOrAdd(ParentMaterial->GetMaterial()).Add(ParamInfo);
}
}
}
}
// Find all direct children of this material (children of children will be discovered later by pushing these children on the MaterialHeap).
TArray<UMaterialInstance*> ParentMaterialInstances;
Task.EnterProgressFrame();
VTConversionWorkerUtil::GetReferencersOfType(ParentMaterial, ParentMaterialInstances);
for (auto MaterialInstance : ParentMaterialInstances)
{
//MaterialInstances.AddUnique(MaterialInstance);
MaterialInferfaces.AddUnique(MaterialInstance);
//InstancesForMaterial.FindOrAdd(MaterialInstance->GetMaterial()).AddUnique(MaterialInstance);
// Push on the heap to check materials referencing us recursively
MaterialHeap.AddUnique(MaterialInstance);
}
}
// We know have a set of root materials and a set of properties to convert to VT
// Find all textures referenced by these properties
// These new textures could in turn be referenced by other materials (not in the inheritance chain)
// which is why we have to run this discovery process iteratively until we don't discover any new
// materials or textures.
for (UMaterialInterface *If : MaterialInferfaces)
{
UMaterial *Mat = If->GetMaterial();
for (const FMaterialParameterInfo &Parameter : ParametersToVtIze.FindOrAdd(Mat))
{
UTexture *Tex = nullptr;
if (If->GetTextureParameterValue(Parameter, Tex))
{
UTexture2D *Tex2D = Cast<UTexture2D>(Tex);
if (Tex2D && !Tex2D->VirtualTextureStreaming)
{
bool bAlreadyExists;
InAffectedTextures.Add(Tex2D, &bAlreadyExists);
if (!bAlreadyExists)
{
AuditTrail.Add(Tex2D, FAuditTrail(
Mat,
FString::Printf(TEXT("set on parameter %s in instance %s of material"), *Parameter.Name.ToString(), *If->GetName())
));
}
}
}
}
}
//
// Pretty much the same again but now for material functions....
//
// Find all materials functions that directly reference the passed in textures
TArray<UMaterialFunctionInterface *> FunctionsUsedByAffectedTextures;
for (UTexture2D* Tex2D : InAffectedTextures)
{
Task.EnterProgressFrame();
if (!Tex2D->GetPathName().StartsWith("/Engine/"))
{
VTConversionWorkerUtil::GetReferencersOfType(Tex2D, FunctionsUsedByAffectedTextures);
}
}
// Find all the root function of the found instances and add them to our
// working lists.
for (UMaterialFunctionInterface *If : FunctionsUsedByAffectedTextures)
{
// It's a material?
UMaterialFunction* Function = If->GetBaseFunction();
if (Function)
{
FunctionInferfaces.AddUnique(Function);
FunctionHeap.AddUnique(Function);
bool bAlreadyExists;
InAffectedFunctions.Add(Function, &bAlreadyExists);
if (!bAlreadyExists)
{
AuditTrail.Add(Function, FAuditTrail(
If,
FString::Printf(TEXT("this is the parent of"))
));
}
}
}
// We have a second class of functions here. That is functions that don't directly reference any textures of interest
// but where there are material instances that overrides properties in the textures that do reference textures of interest
for (TPair<UMaterial*, TSet<FMaterialParameterInfo>>& MaterialParametersPair : ParametersToVtIze)
{
for (FMaterialParameterInfo& Parameter : MaterialParametersPair.Value)
{
TArray<UMaterialFunctionInterface*> DependentFunctions;
MaterialParametersPair.Key->GetDependentFunctions(DependentFunctions);
for (UMaterialFunctionInterface* Function : DependentFunctions)
{
for (UMaterialExpression* FunctionExpression : Function->GetExpressions())
{
if (const UMaterialExpressionTextureSampleParameter* TexParameter = Cast<const UMaterialExpressionTextureSampleParameter>(FunctionExpression))
{
if (TexParameter->ParameterName == Parameter.Name)
{
FunctionParametersToVtIze.FindOrAdd(Function->GetBaseFunction()).Add(Parameter);
InAffectedFunctions.Add(Function->GetBaseFunction());
FunctionHeap.AddUnique(Function->GetBaseFunction());
}
}
}
}
}
}
// We now have a set of "root" functions which will be affected by changing InTextures to VT.
// Now find all children of these materials which will also be affected trough parameters now requiring VT textures being set on them.
// This will again load any child instances and their dependencies which aren't loaded yet
while (FunctionHeap.Num() > 0)
{
UMaterialFunctionInterface *ParentFunction = FunctionHeap[0];
FunctionHeap.RemoveAt(0);
{
UMaterialFunctionInstance* FunctionInstance = Cast<UMaterialFunctionInstance>(ParentFunction);
if (FunctionInstance)
{
// Check all parameters of the current material. If they reference a texture
// we want to convert to VT flag the parameter (this will then cause all textures assigned to this parameter to convert to vt as well)
for (const FTextureParameterValue& TextureParameter : FunctionInstance->TextureParameterValues)
{
UTexture2D* ParamValue2D = Cast<UTexture2D>(TextureParameter.ParameterValue);
if (InAffectedTextures.Contains(ParamValue2D))
{
FunctionParametersToVtIze.FindOrAdd(ParentFunction->GetBaseFunction()).Add(TextureParameter.ParameterInfo);
}
}
}
}
// Find all direct children of this function (children of children will be discovered later by pushing these children on the FunctionHeap).
TArray<UMaterialFunctionInstance*> ParentFunctionInstances;
Task.EnterProgressFrame();
VTConversionWorkerUtil::GetReferencersOfType(ParentFunction, ParentFunctionInstances);
for (auto FunctionInstance : ParentFunctionInstances)
{
FunctionInferfaces.AddUnique(FunctionInstance);
// Push on the heap to check materials referencing us recursively
FunctionHeap.AddUnique(FunctionInstance);
}
}
// We know have a set of root functions and a set of properties to convert to VT
// Find all textures referenced by these properties
// These new textures could in turn be referenced by other materials and or functions (not in the inheritance chain)
// which is why we have to run this discovery process iteratively until we don't discover any new
// materials or textures.
for (UMaterialFunctionInterface *If : FunctionInferfaces)
{
UMaterialFunctionInterface *Func = If->GetBaseFunction();
for (const FMaterialParameterInfo &Parameter : FunctionParametersToVtIze.FindOrAdd(Func))
{
UTexture *Tex = nullptr;
If->OverrideNamedTextureParameter(Parameter, Tex);
UTexture2D *Tex2D = Cast<UTexture2D>(Tex);
if (Tex2D && !Tex2D->VirtualTextureStreaming)
{
bool bAlreadyExists;
InAffectedTextures.Add(Tex2D, &bAlreadyExists);
if (!bAlreadyExists)
{
AuditTrail.Add(Tex2D, FAuditTrail(
Func,
FString::Printf(TEXT("set on parameter %s in instance %s"), *Parameter.Name.ToString(), *If->GetPathName())
));
}
}
}
}
}
void FVirtualTextureConversionWorker::FindAllTexturesAndMaterials(TArray<TObjectPtr<UMaterial >> &OutAffectedMaterials, TArray<TObjectPtr<UMaterialFunctionInterface >> &OutAffectedFunctions, TArray<TObjectPtr<UTexture2D >> &OutAffectedTextures)
{
FScopedSlowTask SlowTask(1000.0f, LOCTEXT("ConvertToVT_Progress_FindAllTexturesAndMaterials", "Finding Textures and Materials..."));
TSet<UMaterial*> AffectedMaterials;
TSet<UMaterialFunctionInterface*> AffectedFunctions;
TSet<UTexture2D*> AffectedTextures;
AffectedMaterials.Append(ObjectPtrDecay(OutAffectedMaterials));
AffectedFunctions.Append(ObjectPtrDecay(OutAffectedFunctions));
AffectedTextures.Append(ObjectPtrDecay(OutAffectedTextures));
int LastNumMaterials = AffectedMaterials.Num();
int LastNumTextures = AffectedTextures.Num();
int LastNumFunctions = AffectedFunctions.Num();
while (true)
{
FindAllTexturesAndMaterials_Iteration(AffectedMaterials, AffectedFunctions, AffectedTextures, MaterialRejectedTextures, SlowTask);
if (AffectedMaterials.Num() == LastNumMaterials && AffectedTextures.Num() == LastNumTextures && AffectedFunctions.Num() == LastNumFunctions)
{
break;
}
LastNumMaterials = AffectedMaterials.Num();
LastNumTextures = AffectedTextures.Num();
LastNumFunctions = AffectedFunctions.Num();
}
OutAffectedMaterials = ObjectPtrWrap(AffectedMaterials.Array());
OutAffectedFunctions = ObjectPtrWrap(AffectedFunctions.Array());
OutAffectedTextures = ObjectPtrWrap(AffectedTextures.Array());
}
void FVirtualTextureConversionWorker::FilterList(int32 SizeThreshold)
{
FScopedSlowTask SlowTask(1.0f, LOCTEXT("ConvertToVT_Progress_FindingTextures", "Finding textures to convert..."));
SlowTask.MakeDialog();
Textures.Empty();
Materials.Empty();
Functions.Empty();
SizeRejectedTextures.Empty();
for (UTexture2D *Texture : UserTextures)
{
bool DoInclude;
if ( !Texture->Source.AreAllBlocksPowerOfTwo() && Texture->PowerOfTwoMode == ETexturePowerOfTwoSetting::None )
{
// not pow2, reject
DoInclude = false;
}
else
{
// don't use Texture->GetSizeX() as it can be Default texture
FIntPoint Size = Texture->Source.GetLogicalSize();
uint64 TexturePixelCount = (uint64) Size.X * Size.Y;
DoInclude = ( TexturePixelCount >= (uint64)SizeThreshold * SizeThreshold );
// for Backwards this should be the other way around
// we want to filter textures *Smaller* than SizeThreshold
if ( bConvertBackward ) DoInclude = ! DoInclude;
}
if ( DoInclude )
{
Textures.Add(Texture);
}
else
{
SizeRejectedTextures.Add(Texture);
}
}
SlowTask.EnterProgressFrame();
FindAllTexturesAndMaterials(Materials, Functions, Textures);
}
void FVirtualTextureConversionWorker::DoConvert()
{
const bool bVirtualTextureEnable = !bConvertBackward;
FScopedSlowTask SlowTask(2.0f, LOCTEXT("ConvertToVT_Progress_ConvertingTexturesAndMaterials", "Converting textures and materials..."));
SlowTask.MakeDialog();
TMap<UTexture2D*, UTexture2D*> EngineTextureToCopyMap;
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Beginning conversion..."));
SlowTask.EnterProgressFrame();
{
FScopedSlowTask TextureTask(static_cast<float>(Textures.Num()), LOCTEXT("ConvertToVT_Progress_TextureTask", "Updating textures..."));
for (UTexture2D *Tex : Textures)
{
UTexture2D* TextureToUpdate = Tex;
if (TextureToUpdate->GetPathName().StartsWith(TEXT("/Engine/")) && TextureToUpdate->GetPackage() != GetTransientPackage())
{
// rather than modify engine content, create a copy and update that
// any materials that we modify will be updated to point to the copy
ObjectTools::FPackageGroupName PGN;
PGN.GroupName = TEXT("");
PGN.ObjectName = Tex->GetName().Append(TEXT("_VT"));
PGN.PackageName = TEXT("/Game/Textures/");
PGN.PackageName.Append(PGN.ObjectName);
UPackage* ExistingPackage = FindPackage(NULL, *PGN.PackageName);
UTexture2D* DuplicateTexture = nullptr;
if (ExistingPackage)
{
UObject* DuplicateObject = StaticFindObject(UTexture2D::StaticClass(), ExistingPackage, *PGN.ObjectName);
if (DuplicateObject)
{
DuplicateTexture = CastChecked<UTexture2D>(DuplicateObject);
}
}
if (!DuplicateTexture || DuplicateTexture->VirtualTextureStreaming != bVirtualTextureEnable)
{
// TODO - overwrite previous texture (if it exists), or should we always generate a unique name?
TSet<UPackage*> ObjectsUserRefusedToFullyLoad;
UObject* DuplicateObject = ObjectTools::DuplicateSingleObject(Tex, PGN, ObjectsUserRefusedToFullyLoad, false);
if (DuplicateObject)
{
DuplicateTexture = CastChecked<UTexture2D>(DuplicateObject);
}
}
TextureToUpdate = DuplicateTexture;
EngineTextureToCopyMap.Add(Tex, TextureToUpdate);
if (!TextureToUpdate)
{
UE_LOG(LogVirtualTextureConversion, Warning, TEXT("Failed to duplicate engine texture %s"), *Tex->GetPathName());
}
}
if (TextureToUpdate)
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Texture %s"), *TextureToUpdate->GetName());
TextureTask.EnterProgressFrame();
if (TextureToUpdate->VirtualTextureStreaming != bVirtualTextureEnable)
{
TextureToUpdate->PreEditChange(nullptr);
TextureToUpdate->VirtualTextureStreaming = bVirtualTextureEnable;
TextureToUpdate->PostEditChange();
}
}
}
}
SlowTask.EnterProgressFrame();
{
FScopedSlowTask MaterialTask(static_cast<float>((Materials.Num() + Functions.Num()) * 2), LOCTEXT("ConvertToVT_Progress_MaterialTask", "Updating materials..."));
TArray<UMaterial*> ModifiedMaterials;
ModifiedMaterials.Reserve(Materials.Num());
TArray<UMaterialFunctionInterface*> ModifiedMaterialFunctions;
ModifiedMaterialFunctions.Reserve(Functions.Num());
for (UMaterial *Mat : Materials)
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Material %s"), *Mat->GetName());
MaterialTask.EnterProgressFrame();
bool MatModified = false;
for (UMaterialExpression *Expr : Mat->GetExpressions())
{
UMaterialExpressionTextureBase *TexExpr = Cast<UMaterialExpressionTextureBase>(Expr);
if (TexExpr)
{
if (Textures.Contains(TexExpr->Texture))
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Adjusting sampler %s."), *TexExpr->GetName());
UTexture2D** FoundTextureCopy = EngineTextureToCopyMap.Find(CastChecked<UTexture2D>(TexExpr->Texture));
UTexture2D* TextureCopy = nullptr;
bool bShouldUpdateMaterial = true;
if (FoundTextureCopy)
{
TextureCopy = *FoundTextureCopy;
if (TextureCopy)
{
TexExpr->Texture = TextureCopy;
}
else
{
// nullptr was set in EngineTextureToCopyMap....this means we failed to create copy of engine texture for this resource
// bail on updating the material in this case
bShouldUpdateMaterial = false;
}
}
auto OldType = TexExpr->SamplerType;
if (bShouldUpdateMaterial)
{
TexExpr->AutoSetSampleType();
}
if (TextureCopy != nullptr || OldType != TexExpr->SamplerType)
{
TexExpr->Modify();
MatModified = true;
}
}
}
}
if (MatModified)
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Material %s added to update list."), *Mat->GetName());
ModifiedMaterials.Add(Mat);
}
else
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Material %s was not modified, skipping."), *Mat->GetName());
}
}
UMaterialEditingLibrary::RecompileMaterials(ModifiedMaterials, UMaterialEditingLibrary::FOnItemComplete::CreateLambda([&MaterialTask]() { MaterialTask.EnterProgressFrame(); }));
for (UMaterialFunctionInterface *Func : Functions)
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Function %s"), *Func->GetName());
MaterialTask.EnterProgressFrame();
bool FuncModified = false;
for (const TObjectPtr<UMaterialExpression>& Expr : Func->GetExpressions())
{
UMaterialExpressionTextureBase *TexExpr = Cast<UMaterialExpressionTextureBase>(Expr);
if (TexExpr)
{
if (Textures.Contains(TexExpr->Texture))
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Adjusting sampler %s."), *TexExpr->GetName());
UTexture2D** FoundTextureCopy = EngineTextureToCopyMap.Find(CastChecked<UTexture2D>(TexExpr->Texture));
UTexture2D* TextureCopy = nullptr;
bool bShouldUpdateMaterial = true;
if (FoundTextureCopy)
{
TextureCopy = *FoundTextureCopy;
if (TextureCopy)
{
TexExpr->Texture = TextureCopy;
}
else
{
// nullptr was set in EngineTextureToCopyMap....this means we failed to create copy of engine texture for this resource
// bail on updating the material in this case
bShouldUpdateMaterial = false;
}
}
auto OldType = TexExpr->SamplerType;
if (bShouldUpdateMaterial)
{
TexExpr->AutoSetSampleType();
}
if (TextureCopy != nullptr || TexExpr->SamplerType != OldType)
{
TexExpr->Modify();
FuncModified = true;
}
}
}
}
if (FuncModified)
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Function %s added to update list."), *Func->GetName());
ModifiedMaterialFunctions.Add(Func);
}
else
{
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Function %s was not modified, skipping."), *Func->GetName());
}
}
UMaterialEditingLibrary::UpdateMaterialFunctions(ModifiedMaterialFunctions, UMaterialEditingLibrary::FOnItemComplete::CreateLambda([&MaterialTask]() { MaterialTask.EnterProgressFrame(); }));
// update the world's viewports
UE_LOG(LogVirtualTextureConversion, Display, TEXT("Broadcasting to editor."));
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
}
}
#undef LOCTEXT_NAMESPACE