void FHierarchicalLODUtilities::DestroyCluster(ALODActor* InActor)
{
	// Find if it has a parent ALODActor
	AActor* Actor = CastChecked<AActor>(InActor);
	UWorld* World = Actor->GetWorld();
	ALODActor* ParentLOD = GetParentLODActor(InActor);

	const FScopedTransaction Transaction(LOCTEXT("UndoAction_DeleteCluster", "Deleting a (invalid) Cluster"));
	Actor->Modify();
	World->Modify();
	if (ParentLOD != nullptr)
	{
		ParentLOD->Modify();
		ParentLOD->RemoveSubActor(Actor);
	}

	// Clean out sub actors and update their LODParent
	while (InActor->SubActors.Num())
	{
		AActor* SubActor = InActor->SubActors[0];
		SubActor->Modify();
		InActor->RemoveSubActor(SubActor);
	}

	// Also destroy the cluster's data
	DestroyClusterData(InActor);

	World->DestroyActor(InActor);

	if (ParentLOD != nullptr && !ParentLOD->HasValidSubActors())
	{
		DestroyCluster(ParentLOD);
	}
}
Exemplo n.º 2
0
FSlateColor HLODOutliner::FLODActorItem::GetTint() const
{
	ALODActor* LODActorPtr = LODActor.Get();
	if (LODActorPtr)
	{
		return LODActorPtr->IsDirty() ? FSlateColor::UseSubduedForeground() : FLinearColor(1.0f, 1.0f, 1.0f);
	}

	return FLinearColor(1.0f, 1.0f, 1.0f);
}
Exemplo n.º 3
0
void HLODOutliner::FLODActorDropTarget::MoveToCluster(AActor* InActor, ALODActor* NewParentActor)
{
	const FScopedTransaction Transaction(LOCTEXT("UndoAction_MoveActorBetweenClusters", "Move Actor between Clusters"));
	InActor->Modify();
	ALODActor* CurrentParentActor = HierarchicalLODUtils::GetParentLODActor(InActor);
	if (CurrentParentActor)
	{
		CurrentParentActor->Modify();
		CurrentParentActor->RemoveSubActor(InActor);
	}
	
	NewParentActor->Modify();
	NewParentActor->AddSubActor(InActor);
	
	GEngine->BroadcastHLODActorMoved(InActor, NewParentActor);
}
void FHierarchicalLODUtilities::DestroyLODActor(ALODActor* InActor)
{
	const FScopedTransaction Transaction(LOCTEXT("UndoAction_DeleteLODActor", "Delete LOD Actor"));
	UWorld* World = InActor->GetWorld();
	World->Modify();
	InActor->Modify();
	
	ALODActor* ParentActor = FHierarchicalLODUtilities::GetParentLODActor(InActor);

	FHierarchicalLODUtilities::DestroyCluster(InActor);
	World->DestroyActor(InActor);

	if (ParentActor && !ParentActor->HasValidSubActors())
	{
		ParentActor->Modify();
		FHierarchicalLODUtilities::DestroyLODActor(ParentActor);
	}
}
ALODActor* FHierarchicalLODUtilities::CreateNewClusterActor(UWorld* InWorld, const int32 InLODLevel, AWorldSettings* WorldSettings)
{
	// Check incoming data
	check(InWorld != nullptr && WorldSettings != nullptr && InLODLevel >= 0);
	if (!WorldSettings->bEnableHierarchicalLODSystem || WorldSettings->HierarchicalLODSetup.Num() == 0 || WorldSettings->HierarchicalLODSetup.Num() < InLODLevel)
	{
		return nullptr;
	}

	// Spawn and setup actor
	ALODActor* NewActor = nullptr;
	NewActor = InWorld->SpawnActor<ALODActor>(ALODActor::StaticClass(), FTransform());
	NewActor->LODLevel = InLODLevel + 1;
	NewActor->LODDrawDistance = 0.0f;
	NewActor->SetStaticMesh(nullptr);

	return NewActor;
}
const bool FHierarchicalLODUtilities::RemoveActorFromCluster(AActor* InActor)
{
	checkf(InActor != nullptr, TEXT("Invalid InActor"));
	
	bool bSucces = false;

	ALODActor* ParentActor = FHierarchicalLODUtilities::GetParentLODActor(InActor);
	if (ParentActor != nullptr)
	{
		const FScopedTransaction Transaction(LOCTEXT("UndoAction_RemoveActorFromCluster", "Remove Actor From Cluster"));
		ParentActor->Modify();
		InActor->Modify();

		bSucces = ParentActor->RemoveSubActor(InActor);

		if (!ParentActor->HasValidSubActors())
		{
			DestroyCluster(ParentActor);
		}
	}
	
	return bSucces;
}
ALODActor* FHierarchicalLODUtilities::CreateNewClusterFromActors(UWorld* InWorld, AWorldSettings* WorldSettings, const TArray<AActor*>& InActors, const int32 InLODLevel /*= 0*/)
{
	checkf(InWorld != nullptr, TEXT("Invalid world"));
	checkf(InActors.Num() > 0, TEXT("Zero number of sub actors"));
	checkf(WorldSettings != nullptr, TEXT("Invalid world settings"));
	checkf(WorldSettings->bEnableHierarchicalLODSystem, TEXT("Hierarchical LOD system is disabled"));

	const FScopedTransaction Transaction(LOCTEXT("UndoAction_CreateNewCluster", "Create new Cluster"));
	InWorld->Modify();

	// Create the cluster
	ALODActor* NewCluster = FHierarchicalLODUtilities::CreateNewClusterActor(InWorld, InLODLevel, WorldSettings);
	checkf(NewCluster != nullptr, TEXT("Failed to create a new cluster"));

	// Add InActors to new cluster
	for (AActor* Actor : InActors)
	{
		checkf(Actor != nullptr, TEXT("Invalid actor in InActors"));
		
		// Check if Actor is currently part of a different cluster
		ALODActor* ParentActor = FHierarchicalLODUtilities::GetParentLODActor(Actor);
		if (ParentActor != nullptr)
		{
			// If so remove it first
			ParentActor->Modify();
			ParentActor->RemoveSubActor(Actor);

			// If the parent cluster is now empty (invalid) destroy it
			if (!ParentActor->HasValidSubActors())
			{
				FHierarchicalLODUtilities::DestroyCluster(ParentActor);
			}
		}

		// Add actor to new cluster
		NewCluster->AddSubActor(Actor);
	}

	// Update sub actor LOD parents to populate 
	NewCluster->UpdateSubActorLODParents();

	return NewCluster;
}
HLODOutliner::FDragValidationInfo HLODOutliner::FLODActorDropTarget::ValidateDrop(FDragDropPayload& DraggedObjects) const
{
	if (DraggedObjects.StaticMeshActors.IsSet() && DraggedObjects.StaticMeshActors->Num() > 0)
	{
		if (DraggedObjects.StaticMeshActors->Num() > 0 && DraggedObjects.LODActors->Num() == 0)
		{			
			if (DraggedObjects.bSceneOutliner == false)
			{
				bool bContaining = false;

				// Check if this StaticMesh Actor is not already inside this cluster
				for (int32 ActorIndex = 0; ActorIndex < DraggedObjects.StaticMeshActors->Num(); ++ActorIndex)
				{
					if (LODActor->SubActors.Contains(DraggedObjects.StaticMeshActors.GetValue()[ActorIndex]))
					{
						bContaining = true;
						break;
					}
				}

				if (!bContaining)
				{
					if (DraggedObjects.StaticMeshActors->Num() > 1)
					{
						return FDragValidationInfo(EHierarchicalLODActionType::MoveActorToCluster, FHLODOutlinerDragDropOp::ToolTip_MultipleSelection_Compatible, LOCTEXT("MoveMultipleToCluster", "Move Actors to Cluster"));
					}
					else
					{
						return FDragValidationInfo(EHierarchicalLODActionType::MoveActorToCluster, FHLODOutlinerDragDropOp::ToolTip_Compatible, LOCTEXT("MoveToCluster", "Move Actor to Cluster"));
					}
				}
				else
				{
					return FDragValidationInfo(EHierarchicalLODActionType::InvalidAction, FHLODOutlinerDragDropOp::ToolTip_Incompatible, LOCTEXT("AlreadyInCluster", "Cannot Add to Existing cluster"));
				}
			}
			else
			{
				TArray<AActor*> Actors;
				for (auto StaticMeshActor : DraggedObjects.StaticMeshActors.GetValue())
				{
					Actors.Add(StaticMeshActor.Get());
				}

				Actors.Add(LODActor.Get());
				
				const bool MultipleActors = DraggedObjects.StaticMeshActors->Num() > 1;
				if (FHierarchicalLODUtilities::AreActorsInSamePersistingLevel(Actors))
				{
					return FDragValidationInfo(EHierarchicalLODActionType::AddActorToCluster, 
						MultipleActors ? FHLODOutlinerDragDropOp::ToolTip_MultipleSelection_Compatible : FHLODOutlinerDragDropOp::ToolTip_Compatible,
						MultipleActors ? LOCTEXT("AddMultipleToCluster", "Add Actors to Cluster") : LOCTEXT("AddToCluster", "Add Actor to Cluster"));
				}
				else
				{
					return FDragValidationInfo(EHierarchicalLODActionType::InvalidAction,
						MultipleActors ? FHLODOutlinerDragDropOp::ToolTip_MultipleSelection_Incompatible : FHLODOutlinerDragDropOp::ToolTip_Incompatible,
						LOCTEXT("NotInSameLODLevel", "Actors are not all in the same persisting level"));
				}				
			}			
		}

		if (DraggedObjects.bSceneOutliner)
		{
			return FDragValidationInfo(EHierarchicalLODActionType::InvalidAction, FHLODOutlinerDragDropOp::ToolTip_Incompatible, LOCTEXT("AlreadyInHLOD", "Actor is already in one of the Hierarchical LOD clusters"));
		}
	}
	else if (DraggedObjects.LODActors.IsSet() && DraggedObjects.LODActors->Num() > 0)
	{
		// Only valid if dragging inside the HLOD outliner
		if (!DraggedObjects.bSceneOutliner)
		{		
			bool bValidForMerge = true;
			bool bValidForChilding = true;
			int32 FirstLODLevel = -1;
			UObject* LevelOuter = nullptr;
			UObject* SubActorOuter = (LODActor->SubActors.Num()) ? LODActor->SubActors[0]->GetOuter() : nullptr;

			for (auto Actor : DraggedObjects.LODActors.GetValue())
			{
				ALODActor* InLODActor = Cast<ALODActor>(Actor.Get());			

				if (InLODActor)
				{
					// If dragged onto self or already containing LODActor early out
					if (InLODActor == LODActor.Get() || LODActor->SubActors.Contains(InLODActor))
					{
						bValidForMerge = false;
						bValidForChilding = false;
						break;
					}

					// Check in case of multiple selected LODActor items to make sure all of them come from the same LOD level
					if (FirstLODLevel == -1)
					{
						FirstLODLevel = InLODActor->LODLevel;
					}

					if (InLODActor->LODLevel != LODActor->LODLevel)
					{
						bValidForMerge = false;

						if (InLODActor->LODLevel != FirstLODLevel)
						{
							bValidForChilding = false;
						}
					}		

					// Check if in same level asset
					if (LevelOuter == nullptr)
					{
						LevelOuter = InLODActor->GetOuter();
					}
					else if (LevelOuter != InLODActor->GetOuter())
					{
						bValidForMerge = false;
						bValidForChilding = false;
					}
					
					if (InLODActor->SubActors.Num() && SubActorOuter != InLODActor->SubActors[0]->GetOuter())
					{
						bValidForChilding = false;
						bValidForMerge = false;
					}					
					
				}
			}

			if (bValidForMerge)
			{
				return FDragValidationInfo(EHierarchicalLODActionType::MergeClusters, (DraggedObjects.LODActors->Num() == 1) ? FHLODOutlinerDragDropOp::ToolTip_Compatible : FHLODOutlinerDragDropOp::ToolTip_MultipleSelection_Compatible, LOCTEXT("MergeHLODClusters", "Merge Cluster(s)"));
			}
			else if (bValidForChilding)
			{
				return FDragValidationInfo(EHierarchicalLODActionType::ChildCluster, (DraggedObjects.LODActors->Num() == 1) ? FHLODOutlinerDragDropOp::ToolTip_Compatible : FHLODOutlinerDragDropOp::ToolTip_MultipleSelection_Compatible, LOCTEXT("ChildHLODClusters", "Add Child Cluster(s)"));
			}
			else
			{
				return FDragValidationInfo(EHierarchicalLODActionType::InvalidAction, FHLODOutlinerDragDropOp::ToolTip_Incompatible, LOCTEXT("InvalidOperation", "Invalid Operation"));
			}
		}
	}

	return FDragValidationInfo(EHierarchicalLODActionType::InvalidAction, FHLODOutlinerDragDropOp::ToolTip_Incompatible, LOCTEXT("NotImplemented", "Not implemented"));
}
Exemplo n.º 9
0
void FLODCluster::BuildActor(ULevel* InLevel, const int32 LODIdx, const bool bCreateMeshes)
{
	FColor Colours[8] = { FColor::Cyan, FColor::Red, FColor::Green, FColor::Blue, FColor::Yellow, FColor::Magenta, FColor::White, FColor::Black };
	// do big size
	if (InLevel && InLevel->GetWorld())
	{
		// create asset using Actors
		const FHierarchicalSimplification& LODSetup = InLevel->GetWorld()->GetWorldSettings()->HierarchicalLODSetup[LODIdx];

		// Retrieve draw distance for current and next LOD level
		const float DrawDistance = LODSetup.DrawDistance;		
		const int32 LODCount = InLevel->GetWorld()->GetWorldSettings()->HierarchicalLODSetup.Num();
		const float NextDrawDistance = (LODIdx < (LODCount - 1)) ? InLevel->GetWorld()->GetWorldSettings()->HierarchicalLODSetup[LODIdx + 1].DrawDistance : 0.0f;

		// Where generated assets will be stored
		UPackage* AssetsOuter = InLevel->GetOutermost(); // this asset is going to save with map, this means, I'll have to delete with it
		if (AssetsOuter)
		{
			TArray<UStaticMeshComponent*> AllComponents;

			for (auto& Actor: Actors)
			{
				TArray<UStaticMeshComponent*> Components;
				
				if (Actor->IsA<ALODActor>())
				{
					HierarchicalLODUtils::ExtractStaticMeshComponentsFromLODActor(Actor, Components);
				}
				else
				{
					Actor->GetComponents<UStaticMeshComponent>(Components);
				}

				// TODO: support instanced static meshes
				Components.RemoveAll([](UStaticMeshComponent* Val){ return Val->IsA(UInstancedStaticMeshComponent::StaticClass()); });

				AllComponents.Append(Components);
			}

			// it shouldn't even have come here if it didn't have any staticmesh
			if (ensure(AllComponents.Num() > 0))
			{
				// In case we don't have outer generated assets should have same path as LOD level

				const FString AssetsPath = AssetsOuter->GetName() + TEXT("/");
				AActor* FirstActor = Actors[0];

				TArray<UObject*> OutAssets;
				FVector OutProxyLocation = FVector::ZeroVector;
				UStaticMesh* MainMesh = nullptr;
				if (bCreateMeshes)
				{
					// Generate proxy mesh and proxy material assets
					IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
					// should give unique name, so use level + actor name
					const FString PackageName = FString::Printf(TEXT("LOD_%s"), *FirstActor->GetName());
					if (MeshUtilities.GetMeshMergingInterface() && LODSetup.bSimplifyMesh)
					{
						MeshUtilities.CreateProxyMesh(Actors, LODSetup.ProxySetting, AssetsOuter, PackageName, OutAssets, OutProxyLocation);
					}
					else
					{						
						MeshUtilities.MergeStaticMeshComponents(AllComponents, FirstActor->GetWorld(), LODSetup.MergeSetting, AssetsOuter, PackageName, LODIdx, OutAssets, OutProxyLocation, LODSetup.DrawDistance, true);
					}

					// we make it private, so it can't be used by outside of map since it's useless, and then remove standalone
					for (auto& AssetIter : OutAssets)
					{
						AssetIter->ClearFlags(RF_Public | RF_Standalone);
					}

					
					// set staticmesh
					for (auto& Asset : OutAssets)
					{
						UStaticMesh* StaticMesh = Cast<UStaticMesh>(Asset);

						if (StaticMesh)
						{
							MainMesh = StaticMesh;
						}
					}
				}
							

				if (MainMesh || !bCreateMeshes)
				{
					UWorld* LevelWorld = Cast<UWorld>(InLevel->GetOuter());

					check (LevelWorld);

					FTransform Transform;
					Transform.SetLocation(OutProxyLocation);					
										
					// create LODActors using the current Actors
					ALODActor* NewActor = nullptr;
										
					NewActor = LevelWorld->SpawnActor<ALODActor>(ALODActor::StaticClass(), Transform);				
					NewActor->SubObjects = OutAssets;
					NewActor->LODLevel = LODIdx+1;
					NewActor->LODDrawDistance = DrawDistance;
					NewActor->SetStaticMesh( MainMesh );
					// now set as parent
					for(auto& Actor : Actors)
					{
						NewActor->AddSubActor(Actor);
					}

					// Mark dirty according to whether or not this is a preview build
					NewActor->SetIsDirty(!bCreateMeshes);
				}
			}
		}
	}
}