void ReattachMaterials(const TArray<FString>& Args) { FMaterialUpdateContext MaterialUpdateContext; UE_LOG(LogConsoleResponse, Display, TEXT("Reattach.Materials:")); // Clear the parents out of combination material for( TObjectIterator<UMaterial> MaterialIt; MaterialIt; ++MaterialIt ) { if(Args.Num() == 1) { UE_LOG(LogConsoleResponse, Display, TEXT(" %s"), *MaterialIt->GetName()); if(MaterialIt->GetName() == Args[0]) { MaterialUpdateContext.AddMaterial(*MaterialIt); } } } UE_LOG(LogConsoleResponse, Display, TEXT("")); }
void UMaterialParameterCollection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { // If the array counts have changed, an element has been added or removed, and we need to update the uniform buffer layout, // Which also requires recompiling any referencing materials if (ScalarParameters.Num() != PreviousScalarParameters.Num() || VectorParameters.Num() != PreviousVectorParameters.Num()) { // Limit the count of parameters to fit within uniform buffer limits const uint32 MaxScalarParameters = 1024; if (ScalarParameters.Num() > MaxScalarParameters) { ScalarParameters.RemoveAt(MaxScalarParameters, ScalarParameters.Num() - MaxScalarParameters); } const uint32 MaxVectorParameters = 1024; if (VectorParameters.Num() > MaxVectorParameters) { VectorParameters.RemoveAt(MaxVectorParameters, VectorParameters.Num() - MaxVectorParameters); } // Generate a new Id so that unloaded materials that reference this collection will update correctly on load StateId = FGuid::NewGuid(); // Update the uniform buffer layout CreateBufferStruct(); // Recreate each instance of this collection for (TObjectIterator<UWorld> It; It; ++It) { UWorld* CurrentWorld = *It; CurrentWorld->AddParameterCollectionInstance(this, false); } // Build set of changed parameter names TSet<FName> ParameterNames; for (const FCollectionVectorParameter& Param : PreviousVectorParameters) { ParameterNames.Add(Param.ParameterName); } for (const FCollectionScalarParameter& Param : PreviousScalarParameters) { ParameterNames.Add(Param.ParameterName); } for (const FCollectionVectorParameter& Param : VectorParameters) { ParameterNames.Remove(Param.ParameterName); } for (const FCollectionScalarParameter& Param : ScalarParameters) { ParameterNames.Remove(Param.ParameterName); } // Create a material update context so we can safely update materials using this parameter collection. { FMaterialUpdateContext UpdateContext; // Go through all materials in memory and recompile them if they use this material parameter collection for (TObjectIterator<UMaterial> It; It; ++It) { UMaterial* CurrentMaterial = *It; bool bRecompile = false; // Preview materials often use expressions for rendering that are not in their Expressions array, // And therefore their MaterialParameterCollectionInfos are not up to date. if (CurrentMaterial->bIsPreviewMaterial) { bRecompile = true; } else { for (int32 FunctionIndex = 0; FunctionIndex < CurrentMaterial->MaterialParameterCollectionInfos.Num() && !bRecompile; FunctionIndex++) { if (CurrentMaterial->MaterialParameterCollectionInfos[FunctionIndex].ParameterCollection == this) { TArray<UMaterialExpressionCollectionParameter*> CollectionParameters; CurrentMaterial->GetAllExpressionsInMaterialAndFunctionsOfType(CollectionParameters); for (UMaterialExpressionCollectionParameter* CollectionParameter : CollectionParameters) { if (ParameterNames.Contains(CollectionParameter->ParameterName)) { bRecompile = true; break; } } } } } if (bRecompile) { UpdateContext.AddMaterial(CurrentMaterial); // Propagate the change to this material CurrentMaterial->PreEditChange(NULL); CurrentMaterial->PostEditChange(); CurrentMaterial->MarkPackageDirty(); } } } } // Update each world's scene with the new instance, and update each instance's uniform buffer to reflect the changes made by the user for (TObjectIterator<UWorld> It; It; ++It) { UWorld* CurrentWorld = *It; CurrentWorld->UpdateParameterCollectionInstances(true); } PreviousScalarParameters.Empty(); PreviousVectorParameters.Empty(); Super::PostEditChangeProperty(PropertyChangedEvent); }
void UTexture::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); SetLightingGuid(); // Determine whether any property that requires recompression of the texture, or notification to Materials has changed. bool RequiresNotifyMaterials = false; UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if( PropertyThatChanged ) { FString PropertyName = *PropertyThatChanged->GetName(); if (FCString::Stricmp(*PropertyName, TEXT("CompressionSettings")) == 0) { RequiresNotifyMaterials = true; } bool bPreventSRGB = (CompressionSettings == TC_Alpha || CompressionSettings == TC_Normalmap || CompressionSettings == TC_Masks || CompressionSettings == TC_HDR); if(bPreventSRGB && SRGB == true) { SRGB = false; } } NumCinematicMipLevels = FMath::Max<int32>( NumCinematicMipLevels, 0 ); if( (PropertyChangedEvent.ChangeType & EPropertyChangeType::Interactive) == 0 ) { // Update the texture resource. This will recache derived data if necessary // which may involve recompressing the texture. UpdateResource(); } // Notify any loaded material instances if changed our compression format if (RequiresNotifyMaterials) { TArray<UMaterialInterface*> MaterialsThatUseThisTexture; // Create a material update context to safely update materials. { FMaterialUpdateContext UpdateContext; // Notify any material that uses this texture TSet<UMaterial*> BaseMaterialsThatUseThisTexture; for (TObjectIterator<UMaterialInterface> It; It; ++It) { UMaterialInterface* MaterialInterface = *It; if (DoesMaterialUseTexture(MaterialInterface,this)) { MaterialsThatUseThisTexture.Add(MaterialInterface); // This is a bit tricky. We want to make sure all materials using this texture are // updated. Materials are always updated. Material instances may also have to be // updated and if they have static permutations their children must be updated // whether they use the texture or not! The safe thing to do is to add the instance's // base material to the update context causing all materials in the tree to update. BaseMaterialsThatUseThisTexture.Add(MaterialInterface->GetMaterial()); } } // Go ahead and update any base materials that need to be. for (TSet<UMaterial*>::TConstIterator It(BaseMaterialsThatUseThisTexture); It; ++It) { UpdateContext.AddMaterial(*It); (*It)->PostEditChange(); } } // Now that all materials and instances have updated send necessary callbacks. for (int32 i = 0; i < MaterialsThatUseThisTexture.Num(); ++i) { FEditorSupportDelegates::MaterialTextureSettingsChanged.Broadcast(MaterialsThatUseThisTexture[i]); } } #if WITH_EDITORONLY_DATA // any texture that is referencing this texture as AssociatedNormalMap needs to be informed { TArray<UTexture*> TexturesThatUseThisTexture; for (TObjectIterator<UTexture> It; It; ++It) { UTexture* Tex = *It; if(Tex != this && Tex->CompositeTexture == this && Tex->CompositeTextureMode != CTM_Disabled) { TexturesThatUseThisTexture.Add(Tex); } } for (int32 i = 0; i < TexturesThatUseThisTexture.Num(); ++i) { TexturesThatUseThisTexture[i]->PostEditChange(); } } #endif }
void UTexture::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); SetLightingGuid(); // Determine whether any property that requires recompression of the texture, or notification to Materials has changed. bool RequiresNotifyMaterials = false; bool DeferCompressionWasEnabled = false; UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if( PropertyThatChanged ) { static const FName CompressionSettingsName("CompressionSettings"); static const FName LODGroupName("LODGroup"); static const FName DeferCompressionName("DeferCompression"); #if WITH_EDITORONLY_DATA static const FName MaxTextureSizeName("MaxTextureSize"); #endif // #if WITH_EDITORONLY_DATA const FName PropertyName = PropertyThatChanged->GetFName(); if (PropertyName == CompressionSettingsName || PropertyName == LODGroupName) { RequiresNotifyMaterials = true; } else if (PropertyName == DeferCompressionName) { DeferCompressionWasEnabled = DeferCompression; } #if WITH_EDITORONLY_DATA else if (PropertyName == MaxTextureSizeName) { if (MaxTextureSize <= 0) { MaxTextureSize = 0; } else { MaxTextureSize = FMath::Min<int32>(FMath::RoundUpToPowerOfTwo(MaxTextureSize), GetMaximumDimension()); } } #endif // #if WITH_EDITORONLY_DATA bool bPreventSRGB = (CompressionSettings == TC_Alpha || CompressionSettings == TC_Normalmap || CompressionSettings == TC_Masks || CompressionSettings == TC_HDR || CompressionSettings == TC_HDR_Compressed); if(bPreventSRGB && SRGB == true) { SRGB = false; } } else { FMaterialUpdateContext UpdateContext; // Update any material that uses this texture TSet<UMaterial*> BaseMaterialsThatUseThisTexture; for (TObjectIterator<UMaterialInterface> It; It; ++It) { UMaterialInterface* MaterialInterface = *It; if (DoesMaterialUseTexture(MaterialInterface, this)) { UMaterial *Material = MaterialInterface->GetMaterial(); bool MaterialAlreadyCompute = false; BaseMaterialsThatUseThisTexture.Add(Material, &MaterialAlreadyCompute); if (!MaterialAlreadyCompute) { UpdateContext.AddMaterial(Material); if (Material->IsTextureForceRecompileCacheRessource(this)) { Material->UpdateMaterialShaderCacheAndTextureReferences(); } } } } //If the DDC key was different the material is already recompile here RequiresNotifyMaterials = false; } NumCinematicMipLevels = FMath::Max<int32>( NumCinematicMipLevels, 0 ); // Don't update the texture resource if we've turned "DeferCompression" on, as this // would cause it to immediately update as an uncompressed texture if( !DeferCompressionWasEnabled && (PropertyChangedEvent.ChangeType & EPropertyChangeType::Interactive) == 0 ) { // Update the texture resource. This will recache derived data if necessary // which may involve recompressing the texture. UpdateResource(); } // Notify any loaded material instances if changed our compression format if (RequiresNotifyMaterials) { TArray<UMaterialInterface*> MaterialsThatUseThisTexture; // Create a material update context to safely update materials. { FMaterialUpdateContext UpdateContext; // Notify any material that uses this texture TSet<UMaterial*> BaseMaterialsThatUseThisTexture; for (TObjectIterator<UMaterialInterface> It; It; ++It) { UMaterialInterface* MaterialInterface = *It; if (DoesMaterialUseTexture(MaterialInterface,this)) { MaterialsThatUseThisTexture.Add(MaterialInterface); // This is a bit tricky. We want to make sure all materials using this texture are // updated. Materials are always updated. Material instances may also have to be // updated and if they have static permutations their children must be updated // whether they use the texture or not! The safe thing to do is to add the instance's // base material to the update context causing all materials in the tree to update. BaseMaterialsThatUseThisTexture.Add(MaterialInterface->GetMaterial()); } } // Go ahead and update any base materials that need to be. for (TSet<UMaterial*>::TConstIterator It(BaseMaterialsThatUseThisTexture); It; ++It) { UpdateContext.AddMaterial(*It); (*It)->PostEditChange(); } } // Now that all materials and instances have updated send necessary callbacks. for (int32 i = 0; i < MaterialsThatUseThisTexture.Num(); ++i) { FEditorSupportDelegates::MaterialTextureSettingsChanged.Broadcast(MaterialsThatUseThisTexture[i]); } } #if WITH_EDITORONLY_DATA // any texture that is referencing this texture as AssociatedNormalMap needs to be informed { TArray<UTexture*> TexturesThatUseThisTexture; for (TObjectIterator<UTexture> It; It; ++It) { UTexture* Tex = *It; if(Tex != this && Tex->CompositeTexture == this && Tex->CompositeTextureMode != CTM_Disabled) { TexturesThatUseThisTexture.Add(Tex); } } for (int32 i = 0; i < TexturesThatUseThisTexture.Num(); ++i) { TexturesThatUseThisTexture[i]->PostEditChange(); } } #endif }