UObject* FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter(UObject* Object, FName& OutName) const { OutName = NAME_None; UObject* Outer = nullptr; UActorComponent* ActorComponent = Cast<UActorComponent>(Object); if (ActorComponent) { //if is child of a BPGC and not child of a CDO UBlueprintGeneratedClass* BPGC = nullptr; for (UObject* OuterObject = ActorComponent->GetOuter(); OuterObject && !BPGC; OuterObject = OuterObject->GetOuter()) { if (OuterObject->HasAnyFlags(RF_ClassDefaultObject)) { return Outer; } BPGC = Cast<UBlueprintGeneratedClass>(OuterObject); } for (UBlueprintGeneratedClass* SuperBPGC = BPGC; SuperBPGC && (OutName == NAME_None); SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperBPGC->GetSuperClass())) { if (SuperBPGC->InheritableComponentHandler) { FComponentKey FoundKey = SuperBPGC->InheritableComponentHandler->FindKey(ActorComponent); if (FoundKey.IsValid()) { OutName = FoundKey.IsSCSKey() ? FoundKey.GetSCSVariableName() : ActorComponent->GetFName(); Outer = BPGC->GetDefaultObject(false); break; } } if (SuperBPGC->SimpleConstructionScript) { for (auto Node : SuperBPGC->SimpleConstructionScript->GetAllNodes()) { if (Node->ComponentTemplate == ActorComponent) { OutName = Node->VariableName; if (OutName != NAME_None) { Outer = BPGC->GetDefaultObject(false); break; } } } } } } if (Outer && (EReplacementResult::ReplaceCompletely == IsTargetedForReplacement(Object->GetClass()))) { UE_LOG(LogBlueprintCodeGen, Log, TEXT("Object '%s' has replaced name '%s' and outer: '%s'"), *GetPathNameSafe(Object), *OutName.ToString(), *GetPathNameSafe(Outer)); return Outer; } return nullptr; }
FTransaction::FObjectRecord::FReferencedObject::FReferencedObject(UObject* InObject) { UActorComponent* Component = Cast<UActorComponent>(InObject); UObject* CDO = nullptr; if (Component && OuterIsCDO(Component, CDO)) { Object = CDO; ComponentName = Component->GetFName(); } else if (Component && Component->IsCreatedByConstructionScript()) { Object = Component->GetOuter(); ComponentName = Component->GetFName(); } else { Object = InObject; } }
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(); } }
void FComponentEditorUtils::CopyComponents(const TArray<UActorComponent*>& ComponentsToCopy) { FStringOutputDevice Archive; const FExportObjectInnerContext Context; // Clear the mark state for saving. UnMarkAllObjects(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp)); // Duplicate the selected component templates into temporary objects that we can modify TMap<FName, FName> ParentMap; TMap<FName, UActorComponent*> ObjectMap; for (UActorComponent* Component : ComponentsToCopy) { // Duplicate the component into a temporary object UObject* DuplicatedComponent = StaticDuplicateObject(Component, GetTransientPackage(), Component->GetFName(), RF_AllFlags & ~RF_ArchetypeObject); if (DuplicatedComponent) { // If the duplicated component is a scene component, wipe its attach parent (to prevent log warnings for referencing a private object in an external package) if (auto DuplicatedCompAsSceneComp = Cast<USceneComponent>(DuplicatedComponent)) { DuplicatedCompAsSceneComp->AttachParent = nullptr; } // Find the closest parent component of the current component within the list of components to copy USceneComponent* ClosestSelectedParent = FindClosestParentInList(Component, ComponentsToCopy); if (ClosestSelectedParent) { // If the parent is included in the list, record it into the node->parent map ParentMap.Add(Component->GetFName(), ClosestSelectedParent->GetFName()); } // Record the temporary object into the name->object map ObjectMap.Add(Component->GetFName(), CastChecked<UActorComponent>(DuplicatedComponent)); } } // Export the component object(s) to text for copying for (auto ObjectIt = ObjectMap.CreateIterator(); ObjectIt; ++ObjectIt) { // Get the component object to be copied UActorComponent* ComponentToCopy = ObjectIt->Value; check(ComponentToCopy); // If this component object had a parent within the selected set if (ParentMap.Contains(ComponentToCopy->GetFName())) { // Get the name of the parent component FName ParentName = ParentMap[ComponentToCopy->GetFName()]; if (ObjectMap.Contains(ParentName)) { // Ensure that this component is a scene component USceneComponent* SceneComponent = Cast<USceneComponent>(ComponentToCopy); if (SceneComponent) { // Set the attach parent to the matching parent object in the temporary set. This allows us to preserve hierarchy in the copied set. SceneComponent->AttachParent = Cast<USceneComponent>(ObjectMap[ParentName]); } } } // Export the component object to the given string UExporter::ExportToOutputDevice(&Context, ComponentToCopy, NULL, Archive, TEXT("copy"), 0, PPF_ExportsNotFullyQualified | PPF_Copy | PPF_Delimited, false, ComponentToCopy->GetOuter()); } // Copy text to clipboard FString ExportedText = Archive; FPlatformMisc::ClipboardCopy(*ExportedText); }
bool UBlueprint::ChangeOwnerOfTemplates() { UBlueprintGeneratedClass* BPGClass = Cast<UBlueprintGeneratedClass>(*GeneratedClass); bool bIsStillStale = false; if (BPGClass) { check(!bIsRegeneratingOnLoad); // >>> Backwards Compatibility: VER_UE4_EDITORONLY_BLUEPRINTS bool bMigratedOwner = false; for( auto CompIt = ComponentTemplates.CreateIterator(); CompIt; ++CompIt ) { UActorComponent* Component = (*CompIt); check(Component); if(Component->GetOuter() == this) { const bool bRenamed = Component->Rename(*Component->GetName(), BPGClass, REN_ForceNoResetLoaders|REN_DoNotDirty); ensure(bRenamed); bIsStillStale |= !bRenamed; bMigratedOwner = true; } } for( auto CompIt = Timelines.CreateIterator(); CompIt; ++CompIt ) { UTimelineTemplate* Template = (*CompIt); check(Template); if(Template->GetOuter() == this) { const FString OldTemplateName = Template->GetName(); ensure(!OldTemplateName.EndsWith(TEXT("_Template"))); const bool bRenamed = Template->Rename(*UTimelineTemplate::TimelineVariableNameToTemplateName(Template->GetFName()), BPGClass, REN_ForceNoResetLoaders|REN_DoNotDirty); ensure(bRenamed); bIsStillStale |= !bRenamed; ensure(OldTemplateName == UTimelineTemplate::TimelineTemplateNameToVariableName(Template->GetFName())); bMigratedOwner = true; } } if(USimpleConstructionScript* SCS = SimpleConstructionScript) { if(SCS->GetOuter() == this) { const bool bRenamed = SCS->Rename(NULL, BPGClass, REN_ForceNoResetLoaders|REN_DoNotDirty); ensure(bRenamed); bIsStillStale |= !bRenamed; bMigratedOwner = true; } } if (bMigratedOwner) { BPGClass->ComponentTemplates = ComponentTemplates; BPGClass->Timelines = Timelines; BPGClass->SimpleConstructionScript = SimpleConstructionScript; } // <<< End Backwards Compatibility } else { UE_LOG(LogBlueprint, Log, TEXT("ChangeOwnerOfTemplates: No BlueprintGeneratedClass in %s"), *GetName()); } return !bIsStillStale; }