//.h
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();
//.CPP
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);
// Initialize SaveAssetDialog config
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);
// Initialize SaveAssetDialog config
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)
{
// USelection* SelectedActors = GEditor->GetSelectedActors();
// // Iterate through selected actors and find first static mesh asset
// // Use this static mesh path as destination package name for a merged mesh
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());
}
}
// // This restriction is only for replacement of selected actors with merged mesh actor
// if (UniqueLevels.Num() > 1 && bReplaceSourceActors)
// {
// FText Message = NSLOCTEXT("UnrealEd", "FailedToMergeActorsSublevels_Msg", "The selected actors should be in the same level");
// const FText Title = NSLOCTEXT("UnrealEd", "FailedToMergeActors_Title", "Unable to merge actors");
// FMessageDialog::Open(EAppMsgType::Ok, Message, &Title);
// return false;
// }
FVector MergedActorLocation;
TArray<UObject*> AssetsToSync;
// Merge...
{
FScopedSlowTask SlowTask(0, LOCTEXT("MergingActorsSlowTask", "Merging actors..."));
SlowTask.MakeDialog();
TArray<UPrimitiveComponent*> ComponentsToMerge;
for (auto PrimComponent : primComponents)
{
bool bShouldIncorporate = false; // Should default to part of merged mesh
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(PrimComponent))
{
bShouldIncorporate = (StaticMeshComponent->GetStaticMesh() != nullptr);
}
else if (UShapeComponent* ShapeComponent = Cast<UShapeComponent>(PrimComponent))
{
bShouldIncorporate = true;
}
// Determine whether or not this component should be incorporated according the user settings
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;
// In this case set to AllLODs value since calculating the LODs is not possible and thus disabled in the UI
Setting.LODSelectionType = EMeshLODSelectionType::AllLODs;
// If the merge destination package already exists, it is possible that the mesh is already used in a scene somewhere, or its materials or even just its textures.
// Static primitives uniform buffers could become invalid after the operation completes and lead to memory corruption. To avoid it, we force a global reregister.
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]);
}
//Also notify the content browser that the new assets exists
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true);
// Place new mesh in the world
if (true) //bReplaceSourceActors
{
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);
// Remove source actors
for (AActor* Actor : Actors)
{
Actor->Destroy();
}
}
}
}
return true;
}
int32 USceneToolHelper::InstancesMergeForWorld(UWorld* World, bool bSelect, bool simpleInstancesMerge)
{
// Gather a list of original actors of merge
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
{
//但凡使用了tag的物体都不参与合批
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)
{
// We only care about actors that are referenced in the world for literals, and also in the same level as this blueprint
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 /*= false*/, bool bActuallyMerge /*= true*/, bool bRemoveOriginal /*= false*/)
{
ULevel* Level = World->GetCurrentLevel();
FMeshInstancingSettings InSettings;
//set InSettings.MeshReplacementMethod
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;
};
// Gather valid components
TArray<UStaticMeshComponent*> ValidComponents;
for (UPrimitiveComponent* ComponentToMerge : ComponentsToMerge)
{
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(ComponentToMerge))
{
// Dont harvest from 'destination' actors
if (StaticMeshComponent->GetOwner()->GetClass() != InSettings.ActorClassToUse.Get())
{
if (!InSettings.bSkipMeshesWithVertexColors || !HasInstanceVertexColors(StaticMeshComponent))
{
ValidComponents.Add(StaticMeshComponent);
}
}
}
}
// if(OutResultsText != nullptr)
// {
// *OutResultsText = LOCTEXT("InstanceMergePredictedResultsNone", "The current settings will not result in any instanced meshes being created");
// }
if (ValidComponents.Num() > 0)
{
/** Helper struct representing a spawned ISMC */
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;
};
// Helper struct representing a spawned ISMC-containing actor
struct FActorEntry
{
FActorEntry(UStaticMeshComponent* InComponent, ULevel* InLevel)
: MergedActor(nullptr)
{
// intersect with HLOD volumes if we have a level
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);
// 获取Actor的UClass
UClass* ActorClass = StaticMeshComponent->GetOwner()->GetClass();
// 取得对应的蓝图
UBlueprint* Blueprint = ActorClass ? Cast<UBlueprint>(ActorClass->ClassGeneratedBy) : nullptr;
if (Blueprint == nullptr)
{
// 检查是否已经存在相同key的组件组
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);
}
// Filter by component count
for (FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.ComponentEntries = ActorEntry.ComponentEntries.FilterByPredicate([&InSettings](const FComponentEntry& InEntry)
{
return InEntry.OriginalComponents.Num() >= InSettings.InstanceReplacementThreshold;
});
}
// Remove any empty actor entries
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(OutResultsText != nullptr)
// {
// *OutResultsText = FText::Format(LOCTEXT("InstanceMergePredictedResults", "The current settings will result in {0} instanced static mesh components ({1} actors will be replaced)"), FText::AsNumber(TotalComponentCount), FText::AsNumber(ActorsToCleanUp.Num()));
// }
if (bActuallyMerge)
{
// Create our actors
const FScopedTransaction Transaction(LOCTEXT("PlaceInstancedActors", "Place Instanced Actor(s)"));
Level->Modify();
FActorSpawnParameters Params;
Params.OverrideLevel = Level;
// We now have the set of component data we want to apply
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())
{
// Attach to root if we already have one
NewComponent->AttachToComponent(ActorEntry.MergedActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
// ActorEntry = FActorEntry(ComponentEntry.OriginalComponents[0], InSettings.bUseHLODVolumes ? Level : nullptr);
// ActorEntry.ComponentEntries = itemActorEntry.ComponentEntries;
// ActorEntry.MergedActor = World->SpawnActor(InSettings.ActorClassToUse.Get(), Params);
}
else
{
// Make a new root if we dont have a root already
ActorEntry.MergedActor->SetRootComponent(NewComponent);
}
// Take 'instanced' ownership so it persists with this actor
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);
}
// Now clean up our original actors
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);
}
}
// pop a toast allowing selection
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 /*= true*/, bool bRemoveOriginal /*= false*/)
{
ULevel* Level = World->GetCurrentLevel();
FMeshInstancingSettings InSettings;
//set InSettings.MeshReplacementMethod
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;
};
// Gather valid components
TArray<UStaticMeshComponent*> ValidComponents;
for(UPrimitiveComponent* ComponentToMerge : ComponentsToMerge)
{
if(UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(ComponentToMerge))
{
// Dont harvest from 'destination' actors
if(StaticMeshComponent->GetOwner()->GetClass() != InSettings.ActorClassToUse.Get())
{
if( !InSettings.bSkipMeshesWithVertexColors || !HasInstanceVertexColors(StaticMeshComponent))
{
ValidComponents.Add(StaticMeshComponent);
}
}
}
}
if(ValidComponents.Num() > 0)
{
/** Helper struct representing a spawned ISMC */
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;
};
/** Helper struct representing a spawned ISMC-containing actor */
struct FActorEntry
{
FActorEntry(UStaticMeshComponent* InComponent, ULevel* InLevel)
: MergedActor(nullptr)
{
// intersect with HLOD volumes if we have a level
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;
};
// Gather a list of components to merge
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);
}
}
// Filter by component count
for(FActorEntry& ActorEntry : ActorEntries)
{
ActorEntry.ComponentEntries = ActorEntry.ComponentEntries.FilterByPredicate([&InSettings](const FComponentEntry& InEntry)
{
return InEntry.OriginalComponents.Num() >= InSettings.InstanceReplacementThreshold;
});
}
// Remove any empty actor entries
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)
{
// Create our actors
const FScopedTransaction Transaction(LOCTEXT("PlaceInstancedActors", "Place Instanced Actor(s)"));
Level->Modify();
FActorSpawnParameters Params;
Params.OverrideLevel = Level;
// We now have the set of component data we want to apply
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())
{
// Attach to root if we already have one
NewComponent->AttachToComponent(ActorEntry.MergedActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
else
{
// Make a new root if we dont have a root already
ActorEntry.MergedActor->SetRootComponent(NewComponent);
}
// Take 'instanced' ownership so it persists with this actor
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);
}
// Now clean up our original actors
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);
}
}
// pop a toast allowing selection
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指针
UWorld* curWorld = GEditor->GetEditorWorldContext().World();
if (curWorld != nullptr)
{
// 获取关卡的长包名(Long Package Name)
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;
// IPlatformFile& PlatformFileSystem = IPlatformFile::GetPlatformPhysical();
for (auto fileToOpen : maps)
{
if (fileToOpen.Contains("Content/__InstanceMap/"))
{
IFileManager::Get().Delete(*fileToOpen, true, false);
// PlatformFileSystem.DeleteFile(*fileToOpen);
}
}
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!"));
}
本文由博客一文多发平台 OpenWrite 发布!