UFUNCTION(BlueprintCallable, Category = "SceneTool")
static TArray<UWorld*> GetAllWorlds(UWorld* world = nullptr, bool bIncludeInWorld = true);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static void UniteTerms(UWorld* world);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static void UniteTermsAllWorlds();
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static FString GetDefaultPackageName(AActor* primitive);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static bool RunMerge(const FString& PackageName, TArray<UPrimitiveComponent*> primComponents);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static int32 InstancesMergeForWorld(UWorld* World, bool bSelect, bool simpleInstancesMerge = true);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static void MergeComponentsToInstances(AActor* parent, TArray<UPrimitiveComponent*> ComponentsToMerge, UWorld* World, bool bSelect = false, bool bActuallyMerge = true, bool bRemoveOriginal = false);
static void DefaultMergeComponentsToInstances(TArray<UPrimitiveComponent*>& ComponentsToMerge, UWorld* World, bool bActuallyMerge = true, bool bRemoveOriginal = false);
UFUNCTION(BlueprintCallable, Category = "SceneTool")
static void PrepPakControl();
TMap<AActor*, TArray<UPrimitiveComponent*>> AddMergeComponents(TMap<AActor*, TArray<UPrimitiveComponent*>> map, TArray<AActor*> attachActors, AActor* parentObj, AActor* actor)
{
TArray<AActor*> itemFloors;
map.GetKeys(itemFloors);
if (itemFloors.Contains(parentObj))
{
if (attachActors.Num() == 0)
{
auto itemstatic = Cast<UStaticMeshComponent>(actor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
map.Find(parentObj)->Add(itemstatic);
}
}
else
{
if (attachActors.Num() == 0)
{
TArray<UPrimitiveComponent*> staticActors;
auto itemstatic = Cast<UStaticMeshComponent>(actor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if (itemstatic)
{
staticActors.Add(itemstatic);
}
map.Add(parentObj, staticActors);
}
}
return map;
}
TArray<UWorld*> USceneToolHelper::GetAllWorlds(UWorld* world, bool bIncludeInWorld)
{
TArray<UWorld*> AllWorlds;
if (world == nullptr)
{
EditorLevelUtils::GetWorlds(GWorld, AllWorlds, bIncludeInWorld);
}
else
{
EditorLevelUtils::GetWorlds(world, AllWorlds, bIncludeInWorld);
}
return AllWorlds;
}
void USceneToolHelper::UniteTerms(UWorld* world)
{
TArray<UPrimitiveComponent*> primComponents;
TArray<FString> packageNames;
TArray<AActor*> actors;
UGameplayStatics::GetAllActorsOfClass(world, AActor::StaticClass(), actors);
for (auto actor : actors)
{
TArray<UPrimitiveComponent*> itemPrimitives;
actor->GetComponents<UPrimitiveComponent>(itemPrimitives);
for (auto primitive : itemPrimitives)
{
if (!primComponents.Contains(primitive))
{
primComponents.Add(primitive);
}
}
const FString DefaultPackageName = GetDefaultPackageName(actor);
if (!packageNames.Contains(DefaultPackageName))
{
packageNames.Add(DefaultPackageName);
}
}
for (auto packageName : packageNames)
{
const FString DefaultPackageName = packageName;
if (DefaultPackageName.Len() > 0)
{
const FString DefaultPath = FPackageName::GetLongPackagePath(DefaultPackageName);
const FString DefaultName = FPackageName::GetShortName(DefaultPackageName);
FSaveAssetDialogConfig SaveAssetDialogConfig;
SaveAssetDialogConfig.DialogTitleOverride = LOCTEXT("CreateMergedActorTitle", "Create Merged Actor");
SaveAssetDialogConfig.DefaultPath = DefaultPath;
SaveAssetDialogConfig.DefaultAssetName = DefaultName;
SaveAssetDialogConfig.ExistingAssetPolicy = ESaveAssetDialogExistingAssetPolicy::AllowButWarn;
SaveAssetDialogConfig.AssetClassNames.Add(UStaticMesh::StaticClass()->GetClassPathName());
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FString SaveObjectPath = ContentBrowserModule.Get().CreateModalSaveAssetDialog(SaveAssetDialogConfig);
if (!SaveObjectPath.IsEmpty())
{
const FString PackageName = FPackageName::ObjectPathToPackageName(SaveObjectPath);
RunMerge(PackageName, primComponents);
}
}
else
{
RunMerge(DefaultPackageName, primComponents);
}
}
}
void USceneToolHelper::UniteTermsAllWorlds()
{
TArray<UWorld*> worlds = GetAllWorlds();
TArray<UPrimitiveComponent*> primComponents;
TArray<FString> packageNames;
for (auto world : worlds)
{
TArray<AActor*> actors;
UGameplayStatics::GetAllActorsOfClass(world, AActor::StaticClass(), actors);
for (auto actor : actors)
{
TArray<UPrimitiveComponent*> itemPrimitives;
actor->GetComponents<UPrimitiveComponent>(itemPrimitives);
for (auto primitive : itemPrimitives)
{
if (!primComponents.Contains(primitive))
{
primComponents.Add(primitive);
}
}
const FString DefaultPackageName = GetDefaultPackageName(actor);
if (!packageNames.Contains(DefaultPackageName))
{
packageNames.Add(DefaultPackageName);
}
}
for (auto packageName : packageNames)
{
const FString DefaultPackageName = packageName;
if (DefaultPackageName.Len() > 0)
{
const FString DefaultPath = FPackageName::GetLongPackagePath(DefaultPackageName);
const FString DefaultName = FPackageName::GetShortName(DefaultPackageName);
FSaveAssetDialogConfig SaveAssetDialogConfig;
SaveAssetDialogConfig.DialogTitleOverride = LOCTEXT("CreateMergedActorTitle", "Create Merged Actor");
SaveAssetDialogConfig.DefaultPath = DefaultPath;
SaveAssetDialogConfig.DefaultAssetName = DefaultName;
SaveAssetDialogConfig.ExistingAssetPolicy = ESaveAssetDialogExistingAssetPolicy::AllowButWarn;
SaveAssetDialogConfig.AssetClassNames.Add(UStaticMesh::StaticClass()->GetClassPathName());
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FString SaveObjectPath = ContentBrowserModule.Get().CreateModalSaveAssetDialog(SaveAssetDialogConfig);
if (!SaveObjectPath.IsEmpty())
{
const FString PackageName = FPackageName::ObjectPathToPackageName(SaveObjectPath);
RunMerge(PackageName, primComponents);
}
}
else
{
RunMerge(DefaultPackageName, primComponents);
}
}
}
}
FString USceneToolHelper::GetDefaultPackageName(AActor* primitive)
{
FString PackageName = FPackageName::FilenameToLongPackageName(FPaths::ProjectContentDir() + TEXT("SM_MERGED"));
if (primitive)
{
FString ActorName = primitive->GetName();
PackageName = FString::Printf(TEXT("%s_%s"), *PackageName, *ActorName);
}
if (PackageName.IsEmpty())
{
PackageName = MakeUniqueObjectName(NULL, UPackage::StaticClass(), *PackageName).ToString();
}
return PackageName;
}
bool USceneToolHelper::RunMerge(const FString& PackageName, TArray<UPrimitiveComponent*> primComponents)
{
const IMeshMergeUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshMergeModule>("MeshMergeUtilities").GetUtilities();
USelection* SelectedActors = GEditor->GetSelectedActors();
TArray<AActor*> Actors;
TArray<ULevel*> UniqueLevels;
for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
{
AActor* Actor = Cast<AActor>(*Iter);
if (Actor)
{
Actors.Add(Actor);
UniqueLevels.AddUnique(Actor->GetLevel());
}
}
FVector MergedActorLocation;
TArray<UObject*> AssetsToSync;
{
FScopedSlowTask SlowTask(0, LOCTEXT("MergingActorsSlowTask", "Merging actors..."));
SlowTask.MakeDialog();
TArray<UPrimitiveComponent*> ComponentsToMerge;
for (auto PrimComponent : primComponents)
{
bool bShouldIncorporate = false;
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(PrimComponent))
{
bShouldIncorporate = (StaticMeshComponent->GetStaticMesh() != nullptr);
}
else if (UShapeComponent* ShapeComponent = Cast<UShapeComponent>(PrimComponent))
{
bShouldIncorporate = true;
}
if (bShouldIncorporate && IsValid(PrimComponent))
{
ComponentsToMerge.Add(PrimComponent);
}
}
if (ComponentsToMerge.Num())
{
UWorld* World = ComponentsToMerge[0]->GetWorld();
checkf(World != nullptr, TEXT("Invalid World retrieved from Mesh components"));
const float ScreenAreaSize = TNumericLimits<float>::Max();
FMeshMergingSettings Setting;
Setting.bMergePhysicsData = true;
Setting.LODSelectionType = EMeshLODSelectionType::AllLODs;
if (FindObject<UObject>(nullptr, *PackageName))
{
FGlobalComponentReregisterContext GlobalReregister;
MeshUtilities.MergeComponentsToStaticMesh(ComponentsToMerge, World, Setting, nullptr, nullptr, PackageName, AssetsToSync, MergedActorLocation, ScreenAreaSize, true);
}
else
{
MeshUtilities.MergeComponentsToStaticMesh(ComponentsToMerge, World, Setting, nullptr, nullptr, PackageName, AssetsToSync, MergedActorLocation, ScreenAreaSize, true);
}
}
}
if (AssetsToSync.Num())
{
FAssetRegistryModule& AssetRegistry = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
int32 AssetCount = AssetsToSync.Num();
for (int32 AssetIndex = 0; AssetIndex < AssetCount; AssetIndex++)
{
AssetRegistry.AssetCreated(AssetsToSync[AssetIndex]);
GEditor->BroadcastObjectReimported(AssetsToSync[AssetIndex]);
}
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true);
if (true)
{
UStaticMesh* MergedMesh = nullptr;
if (AssetsToSync.FindItemByClass(&MergedMesh))
{
const FScopedTransaction Transaction(LOCTEXT("PlaceMergedActor", "Place Merged Actor"));
UniqueLevels[0]->Modify();
UWorld* World = UniqueLevels[0]->OwningWorld;
FActorSpawnParameters Params;
Params.OverrideLevel = UniqueLevels[0];
FRotator MergedActorRotation(ForceInit);
AStaticMeshActor* MergedActor = World->SpawnActor<AStaticMeshActor>(MergedActorLocation, MergedActorRotation, Params);
MergedActor->GetStaticMeshComponent()->SetStaticMesh(MergedMesh);
MergedActor->SetActorLabel(MergedMesh->GetName());
World->UpdateCullDistanceVolumes(MergedActor, MergedActor->GetStaticMeshComponent());
GEditor->SelectNone(true, true);
GEditor->SelectActor(MergedActor, true, true);
for (AActor* Actor : Actors)
{
Actor->Destroy();
}
}
}
}
return true;
}
int32 USceneToolHelper::InstancesMergeForWorld(UWorld* World, bool bSelect, bool simpleInstancesMerge)
{
if (OriginalActorEntries.Num() > 0)
{
OriginalActorEntries.Empty();
}
int32 undoNum = 0;
if (!bSelect)
{
TArray<AActor*> actors;
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), actors);
TArray<UPrimitiveComponent*> other;
TMap<AActor*, TArray<UPrimitiveComponent*>> campusobjs;
TMap<AActor*, TArray<UPrimitiveComponent*>> buildingobjs;
TMap<AActor*, TArray<UPrimitiveComponent*>> floorobjs;
TMap<AActor*, TArray<UPrimitiveComponent*>> roomobjs;
FName thing = TEXT("Thing");
FName campus = TEXT("Campus");
FName building = TEXT("Building");
FName floor = TEXT("Floor");
FName room = TEXT("Room");
for (auto actor : actors)
{
AActor* parentObj = actor->GetAttachParentActor();
TArray<AActor*> attachActors;
actor->GetAttachedActors(attachActors);
if (actor->Tags.Contains(thing))
{
if(simpleInstancesMerge)
{
continue;
}
if (actor->GetAttachParentActor()->Tags.Contains(campus))
{
campusobjs = AddMergeComponents(campusobjs, attachActors, parentObj, actor);
}
else if (actor->GetAttachParentActor()->Tags.Contains(building))
{
buildingobjs = AddMergeComponents(buildingobjs, attachActors, parentObj, actor);
}
else if (actor->GetAttachParentActor()->Tags.Contains(floor))
{
floorobjs = AddMergeComponents(floorobjs, attachActors, parentObj, actor);
}
else if (actor->GetAttachParentActor()->Tags.Contains(room))
{
roomobjs = AddMergeComponents(roomobjs, attachActors, parentObj, actor);
}
}
else
{
if (actor->Tags.Num() == 0)
{
auto itemstatic = Cast<UStaticMeshComponent>(actor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
other.Add(itemstatic);
}
}
}
if (other.Num() > 0)
{
undoNum++;
}
MergeComponentsToInstances(nullptr, other, World, false, true, true);
if (!simpleInstancesMerge)
{
if (campusobjs.Num() > 0)
{
undoNum++;
}
for (auto item : campusobjs)
{
MergeComponentsToInstances(item.Key, item.Value, World, false, true, true);
}
if (buildingobjs.Num() > 0)
{
undoNum++;
}
for (auto item : buildingobjs)
{
MergeComponentsToInstances(item.Key, item.Value, World, false, true, true);
}
if (floorobjs.Num() > 0)
{
undoNum++;
}
for (auto item : floorobjs)
{
MergeComponentsToInstances(item.Key, item.Value, World, false, true, true);
}
if (roomobjs.Num() > 0)
{
undoNum++;
}
for (auto item : roomobjs)
{
MergeComponentsToInstances(item.Key, item.Value, World, false, true, true);
}
}
return undoNum;
}
else
{
TArray<UPrimitiveComponent*> ComponentsToMerge;
USelection* SelectedActors = GEditor->GetSelectedActors();
for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
{
AActor* Actor = Cast<AActor>(*Iter);
auto itemstatic = Cast<UStaticMeshComponent>(Actor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if (itemstatic)
{
ComponentsToMerge.Add(itemstatic);
}
}
DefaultMergeComponentsToInstances(ComponentsToMerge, World, true, true);
return undoNum;
}
}
void USceneToolHelper::MergeComponentsToInstances(AActor* parent, TArray<UPrimitiveComponent*> ComponentsToMerge, UWorld* World, bool bSelect , bool bActuallyMerge , bool bRemoveOriginal )
{
ULevel* Level = World->GetCurrentLevel();
FMeshInstancingSettings InSettings;
if (bRemoveOriginal)
{
InSettings.MeshReplacementMethod = EMeshInstancingReplacementMethod::RemoveOriginalActors;
}
else
{
InSettings.MeshReplacementMethod = EMeshInstancingReplacementMethod::KeepOriginalActorsAsEditorOnly;
}
auto HasInstanceVertexColors = [](UStaticMeshComponent* StaticMeshComponent)
{
for (const FStaticMeshComponentLODInfo& CurrentLODInfo : StaticMeshComponent->LODData)
{
if (CurrentLODInfo.OverrideVertexColors != nullptr || CurrentLODInfo.PaintedVertices.Num() > 0)
{
return true;
}
}
return false;
};
TArray<UStaticMeshComponent*> ValidComponents;
for (UPrimitiveComponent* ComponentToMerge : ComponentsToMerge)
{
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(ComponentToMerge))
{
if (StaticMeshComponent->GetOwner()->GetClass() != InSettings.ActorClassToUse.Get())
{
if (!InSettings.bSkipMeshesWithVertexColors || !HasInstanceVertexColors(StaticMeshComponent))
{
ValidComponents.Add(StaticMeshComponent);
}
}
}
}
if (ValidComponents.Num() > 0)
{
struct FComponentEntry
{
FComponentEntry(UStaticMeshComponent* InComponent)
{
StaticMesh = InComponent->GetStaticMesh();
InComponent->GetUsedMaterials(Materials);
bReverseCulling = InComponent->GetComponentTransform().ToMatrixWithScale().Determinant() < 0.0f;
CollisionProfileName = InComponent->GetCollisionProfileName();
CollisionEnabled = InComponent->GetCollisionEnabled();
OriginalComponents.Add(InComponent);
}
bool operator==(const FComponentEntry& InOther) const
{
return
StaticMesh == InOther.StaticMesh &&
Materials == InOther.Materials &&
bReverseCulling == InOther.bReverseCulling &&
CollisionProfileName == InOther.CollisionProfileName &&
CollisionEnabled == InOther.CollisionEnabled;
}
UStaticMesh* StaticMesh;
TArray<UMaterialInterface*> Materials;
TArray<UStaticMeshComponent*> OriginalComponents;
FName CollisionProfileName;
bool bReverseCulling;
ECollisionEnabled::Type CollisionEnabled;
};
struct FActorEntry
{
FActorEntry(UStaticMeshComponent* InComponent, ULevel* InLevel)
: MergedActor(nullptr)
{
if (InLevel)
{
for (AActor* Actor : InLevel->Actors)
{
if (AHierarchicalLODVolume* HierarchicalLODVolume = Cast<AHierarchicalLODVolume>(Actor))
{
FBox BoundingBox = InComponent->Bounds.GetBox();
FBox VolumeBox = HierarchicalLODVolume->GetComponentsBoundingBox(true);
if (VolumeBox.IsInside(BoundingBox) || (HierarchicalLODVolume->bIncludeOverlappingActors && VolumeBox.Intersect(BoundingBox)))
{
HLODVolume = HierarchicalLODVolume;
break;
}
}
}
}
}
bool operator==(const FActorEntry& InOther) const
{
return HLODVolume == InOther.HLODVolume;
}
TArray<FString> ActorNames;
AActor* MergedActor;
AHierarchicalLODVolume* HLODVolume;
TArray<FComponentEntry> ComponentEntries;
};
TArray<FActorEntry> ItemActorEntries;
for (UStaticMeshComponent* StaticMeshComponent : ValidComponents)
{
int32 ActorEntryIndex = ItemActorEntries.AddUnique(FActorEntry(StaticMeshComponent, InSettings.bUseHLODVolumes ? Level : nullptr));
FActorEntry& ActorEntry = ItemActorEntries[ActorEntryIndex];
FComponentEntry ComponentEntry(StaticMeshComponent);
UClass* ActorClass = StaticMeshComponent->GetOwner()->GetClass();
UBlueprint* Blueprint = ActorClass ? Cast<UBlueprint>(ActorClass->ClassGeneratedBy) : nullptr;
if (Blueprint == nullptr)
{
if (FComponentEntry* ExistingComponentEntry = ActorEntry.ComponentEntries.FindByKey(ComponentEntry))
{
if (!ExistingComponentEntry->OriginalComponents.Contains(StaticMeshComponent))
{
ExistingComponentEntry->OriginalComponents.Add(StaticMeshComponent);
}
}
else
{
ActorEntry.ComponentEntries.Add(ComponentEntry);
}
}
}
TArray<FActorEntry> ActorEntries;
for (auto ComponentEntry : ItemActorEntries[0].ComponentEntries)
{
FActorEntry ActorEntry = FActorEntry(ComponentEntry.OriginalComponents[0], InSettings.bUseHLODVolumes ? Level : nullptr);
ActorEntry.ComponentEntries.Add(ComponentEntry);
ActorEntries.Add(ActorEntry);
}
for (FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.ComponentEntries = ActorEntry.ComponentEntries.FilterByPredicate([&InSettings](const FComponentEntry& InEntry)
{
return InEntry.OriginalComponents.Num() >= InSettings.InstanceReplacementThreshold;
});
}
ActorEntries.RemoveAll([](const FActorEntry& ActorEntry) { return ActorEntry.ComponentEntries.Num() == 0; });
int32 TotalComponentCount = 0;
TArray<AActor*> ActorsToCleanUp;
for (FActorEntry& ActorEntry : ActorEntries)
{
for (const FComponentEntry& ComponentEntry : ActorEntry.ComponentEntries)
{
TotalComponentCount++;
for (UStaticMeshComponent* OriginalComponent : ComponentEntry.OriginalComponents)
{
if (AActor* OriginalActor = OriginalComponent->GetOwner())
{
ActorsToCleanUp.AddUnique(OriginalActor);
}
}
}
}
if (ActorEntries.Num() > 0)
{
if (bActuallyMerge)
{
const FScopedTransaction Transaction(LOCTEXT("PlaceInstancedActors", "Place Instanced Actor(s)"));
Level->Modify();
FActorSpawnParameters Params;
Params.OverrideLevel = Level;
for (FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.MergedActor = World->SpawnActor<AActor>(InSettings.ActorClassToUse.Get(), Params);
for (const FComponentEntry& ComponentEntry : ActorEntry.ComponentEntries)
{
UInstancedStaticMeshComponent* NewComponent = nullptr;
NewComponent = (UInstancedStaticMeshComponent*)ActorEntry.MergedActor->FindComponentByClass(InSettings.ISMComponentToUse.Get());
if (NewComponent && NewComponent->PerInstanceSMData.Num() > 0)
{
NewComponent = nullptr;
}
if (NewComponent == nullptr)
{
NewComponent = NewObject<UInstancedStaticMeshComponent>(ActorEntry.MergedActor, InSettings.ISMComponentToUse.Get());
if (ActorEntry.MergedActor->GetRootComponent())
{
NewComponent->AttachToComponent(ActorEntry.MergedActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
else
{
ActorEntry.MergedActor->SetRootComponent(NewComponent);
}
ActorEntry.MergedActor->RemoveOwnedComponent(NewComponent);
NewComponent->CreationMethod = EComponentCreationMethod::Instance;
ActorEntry.MergedActor->AddOwnedComponent(NewComponent);
}
NewComponent->SetStaticMesh(ComponentEntry.StaticMesh);
for (int32 MaterialIndex = 0; MaterialIndex < ComponentEntry.Materials.Num(); ++MaterialIndex)
{
NewComponent->SetMaterial(MaterialIndex, ComponentEntry.Materials[MaterialIndex]);
}
NewComponent->SetReverseCulling(ComponentEntry.bReverseCulling);
NewComponent->SetCollisionProfileName(ComponentEntry.CollisionProfileName);
NewComponent->SetCollisionEnabled(ComponentEntry.CollisionEnabled);
NewComponent->SetMobility(EComponentMobility::Movable);
for (UStaticMeshComponent* OriginalComponent : ComponentEntry.OriginalComponents)
{
int32 InstanceIndex = NewComponent->AddInstance(OriginalComponent->GetComponentTransform());
FOriginalActorEntry* OriginalActor = new FOriginalActorEntry(OriginalComponent, ActorEntry.MergedActor, InstanceIndex);
if (!OriginalActorEntries.Contains(OriginalActor))
{
if (parent)
{
OriginalActorEntries.Add(OriginalActor);
ActorEntry.MergedActor->Tags.Add(FName("Actor:" + OriginalActor->DisplayName));
}
}
}
NewComponent->RegisterComponent();
}
World->UpdateCullDistanceVolumes(ActorEntry.MergedActor);
}
for (AActor* ActorToCleanUp : ActorsToCleanUp)
{
if (InSettings.MeshReplacementMethod == EMeshInstancingReplacementMethod::RemoveOriginalActors)
{
ActorToCleanUp->Destroy();
}
else if (InSettings.MeshReplacementMethod == EMeshInstancingReplacementMethod::KeepOriginalActorsAsEditorOnly)
{
ActorToCleanUp->Modify();
ActorToCleanUp->bIsEditorOnlyActor = true;
ActorToCleanUp->SetHidden(true);
ActorToCleanUp->bHiddenEd = true;
ActorToCleanUp->SetIsTemporarilyHiddenInEditor(true);
}
}
auto SelectActorsLambda = [ActorEntries]()
{
GEditor->GetSelectedActors()->Modify();
GEditor->GetSelectedActors()->BeginBatchSelectOperation();
GEditor->SelectNone(false, true, false);
for (const FActorEntry& ActorEntry : ActorEntries)
{
GEditor->SelectActor(ActorEntry.MergedActor, true, false, true);
}
GEditor->GetSelectedActors()->EndBatchSelectOperation();
};
FNotificationInfo NotificationInfo(FText::Format(LOCTEXT("CreatedInstancedActorsMessage", "Created {0} Instanced Actor(s)"), FText::AsNumber(ActorEntries.Num())));
NotificationInfo.Hyperlink = FSimpleDelegate::CreateLambda(SelectActorsLambda);
NotificationInfo.HyperlinkText = LOCTEXT("SelectActorsHyperlink", "Select Actors");
NotificationInfo.ExpireDuration = 5.0f;
FSlateNotificationManager::Get().AddNotification(NotificationInfo);
}
}
if (parent)
{
for (auto itemActor : ActorEntries)
{
itemActor.MergedActor->SetActorLabel(parent->GetActorLabel() + "__Instance");
FString Left;
FString Right;
Level->GetFullName().Split(TEXT("."), &Left, &Right);
Right.Split(TEXT(":"), &Left, &Right);
FName tag("__MapName__:" + Left);
itemActor.MergedActor->Tags.Add("Thing");
itemActor.MergedActor->Tags.Add(tag);
auto actorToWorld = itemActor.MergedActor->GetTransform();
itemActor.MergedActor->AttachToActor(parent, FAttachmentTransformRules(EAttachmentRule::KeepRelative, false));
itemActor.MergedActor->SetActorTransform(actorToWorld);
}
}
else
{
for (auto itemActor : ActorEntries)
{
itemActor.MergedActor->SetActorLabel(itemActor.MergedActor->GetActorLabel() + "__Instance");
}
}
}
}
void USceneToolHelper::DefaultMergeComponentsToInstances(TArray<UPrimitiveComponent*>& ComponentsToMerge, UWorld* World, bool bActuallyMerge , bool bRemoveOriginal )
{
ULevel* Level = World->GetCurrentLevel();
FMeshInstancingSettings InSettings;
if (bRemoveOriginal)
{
InSettings.MeshReplacementMethod = EMeshInstancingReplacementMethod::RemoveOriginalActors;
}
else
{
InSettings.MeshReplacementMethod = EMeshInstancingReplacementMethod::KeepOriginalActorsAsEditorOnly;
}
auto HasInstanceVertexColors = [](UStaticMeshComponent* StaticMeshComponent)
{
for (const FStaticMeshComponentLODInfo& CurrentLODInfo : StaticMeshComponent->LODData)
{
if(CurrentLODInfo.OverrideVertexColors != nullptr || CurrentLODInfo.PaintedVertices.Num() > 0)
{
return true;
}
}
return false;
};
TArray<UStaticMeshComponent*> ValidComponents;
for(UPrimitiveComponent* ComponentToMerge : ComponentsToMerge)
{
if(UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(ComponentToMerge))
{
if(StaticMeshComponent->GetOwner()->GetClass() != InSettings.ActorClassToUse.Get())
{
if( !InSettings.bSkipMeshesWithVertexColors || !HasInstanceVertexColors(StaticMeshComponent))
{
ValidComponents.Add(StaticMeshComponent);
}
}
}
}
if(ValidComponents.Num() > 0)
{
struct FComponentEntry
{
FComponentEntry(UStaticMeshComponent* InComponent)
{
StaticMesh = InComponent->GetStaticMesh();
InComponent->GetUsedMaterials(Materials);
bReverseCulling = InComponent->GetComponentTransform().ToMatrixWithScale().Determinant() < 0.0f;
CollisionProfileName = InComponent->GetCollisionProfileName();
CollisionEnabled = InComponent->GetCollisionEnabled();
OriginalComponents.Add(InComponent);
}
bool operator==(const FComponentEntry& InOther) const
{
return
StaticMesh == InOther.StaticMesh &&
Materials == InOther.Materials &&
bReverseCulling == InOther.bReverseCulling &&
CollisionProfileName == InOther.CollisionProfileName &&
CollisionEnabled == InOther.CollisionEnabled;
}
UStaticMesh* StaticMesh;
TArray<UMaterialInterface*> Materials;
TArray<UStaticMeshComponent*> OriginalComponents;
FName CollisionProfileName;
bool bReverseCulling;
ECollisionEnabled::Type CollisionEnabled;
};
struct FActorEntry
{
FActorEntry(UStaticMeshComponent* InComponent, ULevel* InLevel)
: MergedActor(nullptr)
{
if(InLevel)
{
for (AActor* Actor : InLevel->Actors)
{
if (AHierarchicalLODVolume* HierarchicalLODVolume = Cast<AHierarchicalLODVolume>(Actor))
{
FBox BoundingBox = InComponent->Bounds.GetBox();
FBox VolumeBox = HierarchicalLODVolume->GetComponentsBoundingBox(true);
if (VolumeBox.IsInside(BoundingBox) || (HierarchicalLODVolume->bIncludeOverlappingActors && VolumeBox.Intersect(BoundingBox)))
{
HLODVolume = HierarchicalLODVolume;
break;
}
}
}
}
}
bool operator==(const FActorEntry& InOther) const
{
return HLODVolume == InOther.HLODVolume;
}
AActor* MergedActor;
AHierarchicalLODVolume* HLODVolume;
TArray<FComponentEntry> ComponentEntries;
};
TArray<FActorEntry> ActorEntries;
for(UStaticMeshComponent* StaticMeshComponent : ValidComponents)
{
int32 ActorEntryIndex = ActorEntries.AddUnique(FActorEntry(StaticMeshComponent, InSettings.bUseHLODVolumes ? Level : nullptr));
FActorEntry& ActorEntry = ActorEntries[ActorEntryIndex];
FComponentEntry ComponentEntry(StaticMeshComponent);
if(FComponentEntry* ExistingComponentEntry = ActorEntry.ComponentEntries.FindByKey(ComponentEntry))
{
ExistingComponentEntry->OriginalComponents.Add(StaticMeshComponent);
}
else
{
ActorEntry.ComponentEntries.Add(ComponentEntry);
}
}
for(FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.ComponentEntries = ActorEntry.ComponentEntries.FilterByPredicate([&InSettings](const FComponentEntry& InEntry)
{
return InEntry.OriginalComponents.Num() >= InSettings.InstanceReplacementThreshold;
});
}
ActorEntries.RemoveAll([](const FActorEntry& ActorEntry){ return ActorEntry.ComponentEntries.Num() == 0; });
int32 TotalComponentCount = 0;
TArray<AActor*> ActorsToCleanUp;
for(FActorEntry& ActorEntry : ActorEntries)
{
for(const FComponentEntry& ComponentEntry : ActorEntry.ComponentEntries)
{
TotalComponentCount++;
for(UStaticMeshComponent* OriginalComponent : ComponentEntry.OriginalComponents)
{
if(AActor* OriginalActor = OriginalComponent->GetOwner())
{
ActorsToCleanUp.AddUnique(OriginalActor);
}
}
}
}
if(ActorEntries.Num() > 0)
{
if(bActuallyMerge)
{
const FScopedTransaction Transaction(LOCTEXT("PlaceInstancedActors", "Place Instanced Actor(s)"));
Level->Modify();
FActorSpawnParameters Params;
Params.OverrideLevel = Level;
for(FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.MergedActor = World->SpawnActor<AActor>(InSettings.ActorClassToUse.Get(), Params);
for(const FComponentEntry& ComponentEntry : ActorEntry.ComponentEntries)
{
UInstancedStaticMeshComponent* NewComponent = nullptr;
NewComponent = (UInstancedStaticMeshComponent*)ActorEntry.MergedActor->FindComponentByClass(InSettings.ISMComponentToUse.Get());
if (NewComponent && NewComponent->PerInstanceSMData.Num() > 0)
{
NewComponent = nullptr;
}
if (NewComponent == nullptr)
{
NewComponent = NewObject<UInstancedStaticMeshComponent>(ActorEntry.MergedActor, InSettings.ISMComponentToUse.Get());
if (ActorEntry.MergedActor->GetRootComponent())
{
NewComponent->AttachToComponent(ActorEntry.MergedActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
else
{
ActorEntry.MergedActor->SetRootComponent(NewComponent);
}
ActorEntry.MergedActor->RemoveOwnedComponent(NewComponent);
NewComponent->CreationMethod = EComponentCreationMethod::Instance;
ActorEntry.MergedActor->AddOwnedComponent(NewComponent);
}
NewComponent->SetStaticMesh(ComponentEntry.StaticMesh);
for(int32 MaterialIndex = 0; MaterialIndex < ComponentEntry.Materials.Num(); ++MaterialIndex)
{
NewComponent->SetMaterial(MaterialIndex, ComponentEntry.Materials[MaterialIndex]);
}
NewComponent->SetReverseCulling(ComponentEntry.bReverseCulling);
NewComponent->SetCollisionProfileName(ComponentEntry.CollisionProfileName);
NewComponent->SetCollisionEnabled(ComponentEntry.CollisionEnabled);
NewComponent->SetMobility(EComponentMobility::Static);
for(UStaticMeshComponent* OriginalComponent : ComponentEntry.OriginalComponents)
{
NewComponent->AddInstance(OriginalComponent->GetComponentTransform());
}
NewComponent->RegisterComponent();
}
World->UpdateCullDistanceVolumes(ActorEntry.MergedActor);
}
for(AActor* ActorToCleanUp : ActorsToCleanUp)
{
if(InSettings.MeshReplacementMethod == EMeshInstancingReplacementMethod::RemoveOriginalActors)
{
ActorToCleanUp->Destroy();
}
else if(InSettings.MeshReplacementMethod == EMeshInstancingReplacementMethod::KeepOriginalActorsAsEditorOnly)
{
ActorToCleanUp->Modify();
ActorToCleanUp->bIsEditorOnlyActor = true;
ActorToCleanUp->SetHidden(true);
ActorToCleanUp->bHiddenEd = true;
ActorToCleanUp->SetIsTemporarilyHiddenInEditor(true);
}
}
auto SelectActorsLambda = [ActorEntries]()
{
GEditor->GetSelectedActors()->Modify();
GEditor->GetSelectedActors()->BeginBatchSelectOperation();
GEditor->SelectNone(false, true, false);
for(const FActorEntry& ActorEntry : ActorEntries)
{
GEditor->SelectActor(ActorEntry.MergedActor, true, false, true);
}
GEditor->GetSelectedActors()->EndBatchSelectOperation();
};
FNotificationInfo NotificationInfo(FText::Format(LOCTEXT("CreatedInstancedActorsMessage", "Created {0} Instanced Actor(s)"), FText::AsNumber(ActorEntries.Num())));
NotificationInfo.Hyperlink = FSimpleDelegate::CreateLambda(SelectActorsLambda);
NotificationInfo.HyperlinkText = LOCTEXT("SelectActorsHyperlink", "Select Actors");
NotificationInfo.ExpireDuration = 5.0f;
FSlateNotificationManager::Get().AddNotification(NotificationInfo);
}
}
}
}
void USceneToolHelper::PrepPakControl()
{
TArray<FString> maps;
IFileManager::Get().FindFilesRecursive(maps, *FPaths::ProjectContentDir(), TEXT("*.umap"), true, false, false);
UWorld* curWorld = GEditor->GetEditorWorldContext().World();
if (curWorld != nullptr)
{
FString LongPackageName = curWorld->GetOutermost()->GetName();
FString FileSystemPath;
FPackageName::TryConvertLongPackageNameToFilename(LongPackageName, FileSystemPath,TEXT(".umap"));
int32 ElementIndex = maps.IndexOfByPredicate([&](const FString& item) {
return item == FileSystemPath;
});
if (ElementIndex != INDEX_NONE)
{
maps.RemoveAtSwap(ElementIndex);
maps.Add(FileSystemPath);
}
}
TArray<FString> FileToOpen;
TArray<FString> FileToChange;
for (auto fileToOpen : maps)
{
if (fileToOpen.Contains("Content/__InstanceMap/"))
{
IFileManager::Get().Delete(*fileToOpen, true, false);
}
}
const bool bLoadAsTemplate = false;
const bool bShowProgress = true;
FString config = "{";
int32 index = 0;
FString path = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
for (auto itemMap : maps)
{
bool isOpen = FEditorFileUtils::LoadMap(itemMap, bLoadAsTemplate, bShowProgress);
if (isOpen)
{
UWorld* World = GEditor->GetEditorWorldContext().World();
InstancesMergeForWorld(World, false,true);
config = SaveOriginalActorEntries(World, path, config);
FEditorFileUtils::SaveCurrentLevel();
index++;
if (index < FileToOpen.Num())
{
config += ",";
}
}
}
config += "}";
FString pathDrectory = UPakToolHelper::InitPakPath(false);
FString pathDrectoryPIE = UPakToolHelper::InitPakPath(true);
FString filePath = pathDrectory + "MapEntries.json";
FString filePath1 = pathDrectoryPIE + "MapEntries.json";
if (!FPlatformFileManager::Get().Get().GetPlatformFile().DirectoryExists(*pathDrectory))
{
FPlatformFileManager::Get().Get().GetPlatformFile().CreateDirectoryTree(*pathDrectory);
}
if (!FPlatformFileManager::Get().Get().GetPlatformFile().DirectoryExists(*pathDrectoryPIE))
{
FPlatformFileManager::Get().Get().GetPlatformFile().CreateDirectoryTree(*pathDrectoryPIE);
}
FFileHelper::SaveStringToFile(config, *filePath);
FFileHelper::SaveStringToFile(config, *filePath1);
UE_LOG(LogTemp, Log, TEXT("MergeComponentsToInstances Successful!"));
}