void UMaterialInterface::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { // flush the lighting guid on all changes SetLightingGuid(); LightmassSettings.EmissiveBoost = FMath::Max(LightmassSettings.EmissiveBoost, 0.0f); LightmassSettings.DiffuseBoost = FMath::Max(LightmassSettings.DiffuseBoost, 0.0f); LightmassSettings.ExportResolutionScale = FMath::Clamp(LightmassSettings.ExportResolutionScale, 0.0f, 16.0f); LightmassSettings.DistanceFieldPenumbraScale = FMath::Clamp(LightmassSettings.DistanceFieldPenumbraScale, 0.01f, 100.0f); Super::PostEditChangeProperty(PropertyChangedEvent); }
void UMaterialInterface::PostDuplicate(bool bDuplicateForPIE) { Super::PostDuplicate(bDuplicateForPIE); SetLightingGuid(); }
void UStaticMesh::Build(bool bSilent) { #if WITH_EDITOR if (IsTemplate()) return; if (SourceModels.Num() <= 0) { UE_LOG(LogStaticMesh,Warning,TEXT("Static mesh has no source models: %s"),*GetPathName()); return; } if(!bSilent) { FFormatNamedArguments Args; Args.Add( TEXT("Path"), FText::FromString( GetPathName() ) ); const FText StatusUpdate = FText::Format( LOCTEXT("BeginStaticMeshBuildingTask", "({Path}) Building"), Args ); GWarn->BeginSlowTask( StatusUpdate, true ); } // Detach all instances of this static mesh from the scene. FStaticMeshComponentRecreateRenderStateContext RecreateRenderStateContext(this,false); // Release the static mesh's resources. ReleaseResources(); // Flush the resource release commands to the rendering thread to ensure that the build doesn't occur while a resource is still // allocated, and potentially accessing the UStaticMesh. ReleaseResourcesFence.Wait(); // Remember the derived data key of our current render data if any. FString ExistingDerivedDataKey = RenderData ? RenderData->DerivedDataKey : TEXT(""); // Free existing render data and recache. CacheDerivedData(); // Reinitialize the static mesh's resources. InitResources(); // Ensure we have a bodysetup. CreateBodySetup(); check(BodySetup != NULL); #if WITH_EDITOR if( SourceModels.Num() ) { // Rescale simple collision if the user changed the mesh build scale BodySetup->RescaleSimpleCollision( SourceModels[0].BuildSettings.BuildScale3D ); } // Invalidate physics data if this has changed. // TODO_STATICMESH: Not necessary any longer? BodySetup->InvalidatePhysicsData(); BodySetup->CreatePhysicsMeshes(); #endif // Compare the derived data keys to see if renderable mesh data has actually changed. check(RenderData); bool bHasRenderDataChanged = RenderData->DerivedDataKey != ExistingDerivedDataKey; if (bHasRenderDataChanged) { // Warn the user if the new mesh has degenerate tangent bases. if (HasBadTangents(this)) { // Only suggest Recompute Tangents if the import hasn't already tried it FFormatNamedArguments Arguments; Arguments.Add( TEXT("Meshname"), FText::FromString(GetName()) ); Arguments.Add( TEXT("Options"), SourceModels[0].BuildSettings.bRecomputeTangents ? FText::GetEmpty() : LOCTEXT("MeshRecomputeTangents", "Consider enabling Recompute Tangents in the mesh's Build Settings.") ); const FText WarningMsg = FText::Format( LOCTEXT("MeshHasDegenerateTangents", "{Meshname} has degenerate tangent bases which will result in incorrect shading. {Options}"), Arguments ); UE_LOG(LogStaticMesh,Warning,TEXT("%s"),*WarningMsg.ToString()); if (!bSilent) { FMessageDialog::Open(EAppMsgType::Ok, WarningMsg); } } // Force the static mesh to re-export next time lighting is built SetLightingGuid(); // Find any static mesh components that use this mesh and fixup their override colors if necessary. // Also invalidate lighting. *** WARNING components may be reattached here! *** for( TObjectIterator<UStaticMeshComponent> It; It; ++It ) { if ( It->StaticMesh == this ) { It->FixupOverrideColorsIfNecessary( true ); It->InvalidateLightingCache(); } } } if(!bSilent) { GWarn->EndSlowTask(); } #else UE_LOG(LogStaticMesh,Fatal,TEXT("UStaticMesh::Build should not be called on non-editor builds.")); #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; 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 }