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; } }
bool FComponentEditorUtils::CanCopyComponents(const TArray<UActorComponent*>& ComponentsToCopy) { bool bCanCopy = ComponentsToCopy.Num() > 0; if (bCanCopy) { for (int32 i = 0; i < ComponentsToCopy.Num() && bCanCopy; ++i) { // Check for the default scene root; that cannot be copied/duplicated UActorComponent* Component = ComponentsToCopy[i]; bCanCopy = Component != nullptr && Component->GetFName() != USceneComponent::GetDefaultSceneRootVariableName(); if (bCanCopy) { UClass* ComponentClass = Component->GetClass(); check(ComponentClass != nullptr); // Component class cannot be abstract and must also be tagged as BlueprintSpawnable bCanCopy = !ComponentClass->HasAnyClassFlags(CLASS_Abstract) && ComponentClass->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent); } } } return bCanCopy; }
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); }
void USimpleConstructionScript::FixupRootNodeParentReferences() { // Get the BlueprintGeneratedClass that owns the SCS UClass* BPGeneratedClass = GetOwnerClass(); if(BPGeneratedClass == NULL) { UE_LOG(LogBlueprint, Warning, TEXT("USimpleConstructionScript::FixupRootNodeParentReferences() - owner class is NULL; skipping.")); // cannot do the rest of fixup without a BPGC return; } for (int32 NodeIndex=0; NodeIndex < RootNodes.Num(); ++NodeIndex) { // If this root node is parented to a native/inherited component template USCS_Node* RootNode = RootNodes[NodeIndex]; if(RootNode->ParentComponentOrVariableName != NAME_None) { bool bWasFound = false; // If the node is parented to a native component if(RootNode->bIsParentComponentNative) { // Get the Blueprint class default object AActor* CDO = Cast<AActor>(BPGeneratedClass->GetDefaultObject(false)); if(CDO != NULL) { // Look for the parent component in the CDO's components array TInlineComponentArray<UActorComponent*> Components; CDO->GetComponents(Components); for (auto CompIter = Components.CreateConstIterator(); CompIter && !bWasFound; ++CompIter) { UActorComponent* ComponentTemplate = *CompIter; bWasFound = ComponentTemplate->GetFName() == RootNode->ParentComponentOrVariableName; } } else { // SCS and BGClass depends on each other (while their construction). // Class is not ready, so one have to break the dependency circle. continue; } } // Otherwise the node is parented to an inherited SCS node from a parent Blueprint else { // Get the Blueprint hierarchy TArray<const UBlueprintGeneratedClass*> ParentBPClassStack; const bool bErrorFree = UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(BPGeneratedClass, ParentBPClassStack); // Find the parent Blueprint in the hierarchy for(int32 StackIndex = ParentBPClassStack.Num() - 1; StackIndex > 0; --StackIndex) { const UBlueprintGeneratedClass* ParentClass = ParentBPClassStack[StackIndex]; if( ParentClass != NULL && ParentClass->SimpleConstructionScript != NULL && ParentClass->GetFName() == RootNode->ParentComponentOwnerClassName) { // Attempt to locate a match by searching all the nodes that belong to the parent Blueprint's SCS for (USCS_Node* ParentNode : ParentClass->SimpleConstructionScript->GetAllNodes()) { if (ParentNode != nullptr && ParentNode->VariableName == RootNode->ParentComponentOrVariableName) { bWasFound = true; break; } } // We found a match; no need to continue searching the hierarchy break; } } } // Clear parent info if we couldn't find the parent component instance if(!bWasFound) { UE_LOG(LogBlueprint, Warning, TEXT("USimpleConstructionScript::FixupRootNodeParentReferences() - Couldn't find %s parent component '%s' for '%s' in BlueprintGeneratedClass '%s' (it may have been removed)"), RootNode->bIsParentComponentNative ? TEXT("native") : TEXT("inherited"), *RootNode->ParentComponentOrVariableName.ToString(), *RootNode->GetVariableName().ToString(), *BPGeneratedClass->GetName()); RootNode->bIsParentComponentNative = false; RootNode->ParentComponentOrVariableName = NAME_None; RootNode->ParentComponentOwnerClassName = NAME_None; } } } // call this after we do the above ParentComponentOrVariableName fixup, // because this operates differently for root nodes that have their // ParentComponentOrVariableName field cleared // // repairs invalid scene hierarchies (like when this Blueprint has been // reparented and there is no longer an inherited scene root... meaning one // of the scene component nodes here needs to be promoted) FixupSceneNodeHierarchy(); }