UE中计:UE4合并静态网格体实例

//.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 发布!

你可能感兴趣的:(UE)