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; }
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); } } } } }