// Copyright Epic Games, Inc. All Rights Reserved. #include "MergeProxyUtils/Utils.h" #include "Misc/PackageName.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SCompoundWidget.h" #include "Components/StaticMeshComponent.h" #include "Widgets/Views/STableViewBase.h" #include "Widgets/Views/STableRow.h" #include "Widgets/Input/SCheckBox.h" #include "Engine/Selection.h" #include "Editor.h" #include "Components/ChildActorComponent.h" #include "Components/ShapeComponent.h" #define LOCTEXT_NAMESPACE "MergeProxyDialog" void BuildMergeComponentDataFromSelection(TArray>& OutComponentsData, bool bAllowShapeComponents) { OutComponentsData.Empty(); // Retrieve selected actors USelection* SelectedActors = GEditor->GetSelectedActors(); TSet Actors; for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter) { AActor* Actor = Cast(*Iter); if (Actor) { Actors.Add(Actor); // Add child actors & actors found under foundations Actor->EditorGetUnderlyingActors(Actors); } } for (AActor* Actor : Actors) { check(Actor != nullptr); TArray PrimComponents; Actor->GetComponents(PrimComponents); for (UPrimitiveComponent* PrimComponent : PrimComponents) { bool bInclude = false; // Should put into UI list bool bShouldIncorporate = false; // Should default to part of merged mesh bool bIsMesh = false; if (UStaticMeshComponent* StaticMeshComponent = Cast(PrimComponent)) { bShouldIncorporate = (StaticMeshComponent->GetStaticMesh() != nullptr); bInclude = true; bIsMesh = true; } else if (UShapeComponent* ShapeComponent = Cast(PrimComponent)) { if (bAllowShapeComponents) { bShouldIncorporate = true; bInclude = true; } } if (bInclude) { OutComponentsData.Add(TSharedPtr(new FMergeComponentData(PrimComponent))); TSharedPtr& ComponentData = OutComponentsData.Last(); ComponentData->bShouldIncorporate = bShouldIncorporate; } } } } void BuildActorsListFromMergeComponentsData(const TArray>& InComponentsData, TArray& OutActors, TArray* OutLevels /* = nullptr */) { for (const TSharedPtr& SelectedComponent : InComponentsData) { if (SelectedComponent->PrimComponent.IsValid()) { AActor* Actor = SelectedComponent->PrimComponent.Get()->GetOwner(); OutActors.AddUnique(Actor); if (OutLevels) OutLevels->AddUnique(Actor->GetLevel()); } } } bool GetPackageNameForMergeAction(const FString& DefaultPackageName, FString& OutPackageName) { if (DefaultPackageName.Len() > 0) { const FString DefaultPath = FPackageName::GetLongPackagePath(DefaultPackageName); const FString DefaultName = FPackageName::GetShortName(DefaultPackageName); // Initialize SaveAssetDialog config FSaveAssetDialogConfig SaveAssetDialogConfig; SaveAssetDialogConfig.DialogTitleOverride = LOCTEXT("CreateMergedActorTitle", "Create Merged Actor"); SaveAssetDialogConfig.DefaultPath = DefaultPath; SaveAssetDialogConfig.DefaultAssetName = DefaultName; SaveAssetDialogConfig.ExistingAssetPolicy = ESaveAssetDialogExistingAssetPolicy::AllowButWarn; SaveAssetDialogConfig.AssetClassNames = { UStaticMesh::StaticClass()->GetClassPathName() }; FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); FString SaveObjectPath = ContentBrowserModule.Get().CreateModalSaveAssetDialog(SaveAssetDialogConfig); if (!SaveObjectPath.IsEmpty()) { OutPackageName = FPackageName::ObjectPathToPackageName(SaveObjectPath); return true; } else { return false; } } else { OutPackageName = DefaultPackageName; return true; } } void FComponentSelectionControl::UpdateSelectedCompnentsAndListBox() { StoreCheckBoxState(); UpdateSelectedStaticMeshComponents(); ComponentsListView->ClearSelection(); ComponentsListView->RequestListRefresh(); } void FComponentSelectionControl::StoreCheckBoxState() { StoredCheckBoxStates.Empty(); // Loop over selected mesh component and store its checkbox state for (TSharedPtr SelectedComponent : SelectedComponents) { UPrimitiveComponent* PrimComponent = SelectedComponent->PrimComponent.Get(); const ECheckBoxState State = SelectedComponent->bShouldIncorporate ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; StoredCheckBoxStates.Add(PrimComponent, State); } } void FComponentSelectionControl::UpdateSelectedStaticMeshComponents() { BuildMergeComponentDataFromSelection(SelectedComponents, bAllowShapeComponents); // Count number of selected objects & Update check state based on previous data NumSelectedMeshComponents = 0; for (TSharedPtr& ComponentData : SelectedComponents) { ECheckBoxState* StoredState = StoredCheckBoxStates.Find(ComponentData->PrimComponent.Get()); if (StoredState != nullptr) { ComponentData->bShouldIncorporate = (*StoredState == ECheckBoxState::Checked); } // Keep count of selected meshes const bool bIsMesh = (Cast(ComponentData->PrimComponent.Get()) != nullptr); if (ComponentData->bShouldIncorporate && bIsMesh) { NumSelectedMeshComponents++; } } } TSharedRef FComponentSelectionControl::MakeComponentListItemWidget(TSharedPtr ComponentData, const TSharedRef& OwnerTable) { check(ComponentData->PrimComponent != nullptr); // If box should be enabled bool bEnabled = true; bool bIsMesh = false; // See if we stored a checkbox state for this mesh component, and set accordingly ECheckBoxState State = bEnabled ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; ECheckBoxState* StoredState = StoredCheckBoxStates.Find(ComponentData->PrimComponent.Get()); if (StoredState) { State = *StoredState; } if (UStaticMeshComponent* StaticMeshComponent = Cast(ComponentData->PrimComponent.Get())) { bEnabled = (StaticMeshComponent->GetStaticMesh() != nullptr); bIsMesh = true; } return SNew(STableRow>, OwnerTable) [ SNew(SBox) [ // Disable UI element if this static mesh component has invalid static mesh data SNew(SHorizontalBox) .IsEnabled(bEnabled) + SHorizontalBox::Slot() .AutoWidth() [ SNew(SCheckBox) .IsChecked(State) .ToolTipText(LOCTEXT("IncorporateCheckBoxToolTip", "When ticked the Component will be incorporated into the merge")) .OnCheckStateChanged_Lambda([this, ComponentData, bIsMesh](ECheckBoxState NewState) { ComponentData->bShouldIncorporate = (NewState == ECheckBoxState::Checked); if (bIsMesh) { this->NumSelectedMeshComponents += (NewState == ECheckBoxState::Checked) ? 1 : -1; } }) ] + SHorizontalBox::Slot() .Padding(5.0, 0, 0, 0) .AutoWidth() [ SNew(STextBlock) .Text_Lambda([ComponentData]() -> FText { if (ComponentData.IsValid() && ComponentData->PrimComponent.IsValid()) { const FString OwningActorName = ComponentData->PrimComponent->GetOwner()->GetName(); const FString ComponentName = ComponentData->PrimComponent->GetName(); FString ComponentInfo; if (UStaticMeshComponent* StaticMeshComponent = Cast(ComponentData->PrimComponent.Get())) { ComponentInfo = (StaticMeshComponent->GetStaticMesh() != nullptr) ? StaticMeshComponent->GetStaticMesh()->GetName() : TEXT("No Static Mesh Available"); } else if (UShapeComponent* ShapeComponent = Cast(ComponentData->PrimComponent.Get())) { ComponentInfo = ShapeComponent->GetClass()->GetName(); } return FText::FromString(OwningActorName + " - " + ComponentInfo + " - " + ComponentName); } return FText::FromString("Invalid Actor"); }) ] ] ]; } #undef LOCTEXT_NAMESPACE