void FComponentMaterialCategory::OnGetMaterialsForView( IMaterialListBuilder& MaterialList ) { const bool bAllowNullEntries = true; // Iterate over every material on the actors for( FMaterialIterator It( SelectedComponents ); It; ++It ) { int32 MaterialIndex = It.GetMaterialIndex(); UActorComponent* CurrentComponent = It.GetComponent(); if( CurrentComponent ) { UMaterialInterface* Material = It.GetMaterial(); AActor* Actor = CurrentComponent->GetOwner(); // Component materials can be replaced if the component supports material overrides const bool bCanBeReplaced = ( CurrentComponent->IsA( UMeshComponent::StaticClass() ) || CurrentComponent->IsA( UTextRenderComponent::StaticClass() ) || CurrentComponent->IsA( ULandscapeComponent::StaticClass() ) ); // Add the material if we allow null materials to be added or we have a valid material if( bAllowNullEntries || Material ) { MaterialList.AddMaterial( MaterialIndex, Material, bCanBeReplaced ); } } } }
void FComponentMaterialCategory::OnMaterialChanged( UMaterialInterface* NewMaterial, UMaterialInterface* PrevMaterial, int32 SlotIndex, bool bReplaceAll ) { // Whether or not we should begin a transaction on swap // Note we only begin a transaction on the first swap bool bShouldMakeTransaction = true; // Whether or not we made a transaction and need to end it bool bMadeTransaction = false; // Lambda to swap materials on a given component at the given slot index auto SwapMaterialLambda = []( UActorComponent* InComponent, int32 InElementIndex, UMaterialInterface* InNewMaterial ) { UPrimitiveComponent* PrimitiveComp = Cast<UPrimitiveComponent>( InComponent ); UDecalComponent* DecalComponent = Cast<UDecalComponent>( InComponent ); if( PrimitiveComp ) { PrimitiveComp->SetMaterial( InElementIndex, InNewMaterial ); } else if( DecalComponent ) { DecalComponent->SetMaterial( InElementIndex, InNewMaterial ); } }; // Scan the selected actors mesh components for the old material and swap it with the new material for( FMaterialIterator It( SelectedComponents ); It; ++It ) { int32 MaterialIndex = It.GetMaterialIndex(); UActorComponent* CurrentComponent = It.GetComponent(); if( CurrentComponent ) { // Component materials can be replaced if they are not created from a blueprint (not exposed to the user) and have material overrides on the component bool bCanBeReplaced = ( CurrentComponent->IsA( UMeshComponent::StaticClass() ) || CurrentComponent->IsA( UDecalComponent::StaticClass() ) || CurrentComponent->IsA( UTextRenderComponent::StaticClass() ) || CurrentComponent->IsA( ULandscapeComponent::StaticClass() ) ); UMaterialInterface* Material = It.GetMaterial(); // Check if the material is the same as the previous material or we are replaceing all in the same slot. If so we will swap it with the new material if( bCanBeReplaced && ( Material == PrevMaterial || bReplaceAll ) && It.GetMaterialIndex() == SlotIndex ) { // Begin a transaction for undo/redo the first time we encounter a material to replace. // There is only one transaction for all replacement if( bShouldMakeTransaction && !bMadeTransaction ) { GEditor->BeginTransaction( NSLOCTEXT("UnrealEd", "ReplaceComponentUsedMaterial", "Replace component used material") ); bMadeTransaction = true; } UProperty* MaterialProperty = NULL; UObject* EditChangeObject = CurrentComponent; if( CurrentComponent->IsA( UMeshComponent::StaticClass() ) ) { MaterialProperty = FindField<UProperty>( UMeshComponent::StaticClass(), "OverrideMaterials" ); } else if( CurrentComponent->IsA( UDecalComponent::StaticClass() ) ) { MaterialProperty = FindField<UProperty>( UDecalComponent::StaticClass(), "DecalMaterial" ); } else if( CurrentComponent->IsA( UTextRenderComponent::StaticClass() ) ) { MaterialProperty = FindField<UProperty>( UTextRenderComponent::StaticClass(), "TextMaterial" ); } else if (CurrentComponent->IsA<ULandscapeComponent>() ) { MaterialProperty = FindField<UProperty>( ALandscapeProxy::StaticClass(), "LandscapeMaterial" ); EditChangeObject = CastChecked<ULandscapeComponent>(CurrentComponent)->GetLandscapeProxy(); } // Add a navigation update lock only if the component world is valid TSharedPtr<FNavigationLockContext> NavUpdateLock; UWorld* World = CurrentComponent->GetWorld(); if( World ) { NavUpdateLock = MakeShareable( new FNavigationLockContext(World, ENavigationLockReason::MaterialUpdate) ); } EditChangeObject->PreEditChange( MaterialProperty ); if( NotifyHook && MaterialProperty ) { NotifyHook->NotifyPreChange( MaterialProperty ); } SwapMaterialLambda( CurrentComponent, It.GetMaterialIndex(), NewMaterial ); FPropertyChangedEvent PropertyChangedEvent( MaterialProperty ); EditChangeObject->PostEditChangeProperty( PropertyChangedEvent ); if( NotifyHook && MaterialProperty ) { NotifyHook->NotifyPostChange( PropertyChangedEvent, MaterialProperty ); } // Propagate material change to instances of the edited component template if( !FApp::IsGame() ) { TArray<UObject*> ComponentArchetypeInstances; if( CurrentComponent->HasAnyFlags(RF_ArchetypeObject) ) { CurrentComponent->GetArchetypeInstances(ComponentArchetypeInstances); } else if( UObject* Outer = CurrentComponent->GetOuter() ) { TArray<UObject*> OuterArchetypeInstances; Outer->GetArchetypeInstances(OuterArchetypeInstances); for( auto OuterArchetypeInstance : OuterArchetypeInstances ) { if( UObject* ArchetypeInstance = static_cast<UObject*>(FindObjectWithOuter(OuterArchetypeInstance, CurrentComponent->GetClass(), CurrentComponent->GetFName())) ) { ComponentArchetypeInstances.Add(ArchetypeInstance); } } } for( auto ComponentArchetypeInstance : ComponentArchetypeInstances ) { CurrentComponent = CastChecked<UActorComponent>( ComponentArchetypeInstance ); if( CurrentComponent->IsA<ULandscapeComponent>() ) { ComponentArchetypeInstance = CastChecked<ULandscapeComponent>(CurrentComponent)->GetLandscapeProxy(); } // Reset the navigation update lock if necessary UWorld* PreviousWorld = World; World = CurrentComponent->GetWorld(); if( PreviousWorld != World ) { NavUpdateLock = MakeShareable( new FNavigationLockContext(World, ENavigationLockReason::MaterialUpdate) ); } ComponentArchetypeInstance->PreEditChange( MaterialProperty ); SwapMaterialLambda( CurrentComponent, It.GetMaterialIndex(), NewMaterial ); ComponentArchetypeInstance->PostEditChangeProperty( PropertyChangedEvent ); } } } } } if( bMadeTransaction ) { // End the transation if we created one GEditor->EndTransaction(); // Redraw viewports to reflect the material changes GUnrealEd->RedrawLevelEditingViewports(); } }