// Copyright Epic Games, Inc. All Rights Reserved. #include "HLODBuilderMeshApproximate.h" #include "Algo/ForEach.h" #include "Algo/Transform.h" #include "WorldPartition/HLOD/HLODActor.h" #include "WorldPartition/HLOD/HLODLayer.h" #include "Engine/StaticMesh.h" #include "Materials/Material.h" #include "Materials/MaterialInstance.h" #include "MaterialUtilities.h" #include "Modules/ModuleManager.h" #include "IGeometryProcessingInterfacesModule.h" #include "GeometryProcessingInterfaces/ApproximateActors.h" #include "HLODBuilderInstancing.h" TArray FHLODBuilder_MeshApproximate::CreateComponents(AWorldPartitionHLOD* InHLODActor, const UHLODLayer* InHLODLayer, const TArray& InSubComponents) { TRACE_CPUPROFILER_EVENT_SCOPE(FHLODBuilder_MeshApproximate::CreateComponents); TSet Actors; TArray InstancedComponents; // Filter the input components for (UPrimitiveComponent* SubComponent : InSubComponents) { if (!SubComponent) { continue; } switch (SubComponent->HLODBatchingPolicy) { case EHLODBatchingPolicy::None: Actors.Add(SubComponent->GetOwner()); break; case EHLODBatchingPolicy::Instancing: InstancedComponents.Add(SubComponent); break; case EHLODBatchingPolicy::MeshSection: InstancedComponents.Add(SubComponent); UE_LOG(LogHLODBuilder, Warning, TEXT("EHLODBatchingPolicy::MeshSection is not yet supported by the MeshApproximate builder.")); break; } } IGeometryProcessingInterfacesModule& GeomProcInterfaces = FModuleManager::Get().LoadModuleChecked("GeometryProcessingInterfaces"); IGeometryProcessing_ApproximateActors* ApproxActorsAPI = GeomProcInterfaces.GetApproximateActorsImplementation(); // // Construct options for ApproximateActors operation // const FMeshApproximationSettings& UseSettings = InHLODLayer->GetMeshApproximationSettings(); IGeometryProcessing_ApproximateActors::FOptions Options = ApproxActorsAPI->ConstructOptions(UseSettings); Options.BasePackagePath = InHLODActor->GetPackage()->GetName(); Options.bGenerateLightmapUVs = false; Options.bCreatePhysicsBody = false; // Material baking settings Options.BakeMaterial = GEngine->DefaultHLODFlattenMaterial; Options.BaseColorTexParamName = FName("BaseColorTexture"); Options.NormalTexParamName = FName("NormalTexture"); Options.MetallicTexParamName = FName("MetallicTexture"); Options.RoughnessTexParamName = FName("RoughnessTexture"); Options.SpecularTexParamName = FName("SpecularTexture"); Options.EmissiveTexParamName = FName("EmissiveHDRTexture"); Options.bUsePackedMRS = true; Options.PackedMRSTexParamName = FName("PackedTexture"); // Gather bounds of the input components auto GetActorsBounds = [&]() -> FBoxSphereBounds { FBoxSphereBounds Bounds; bool bFirst = true; for (UPrimitiveComponent* Component : InSubComponents) { FBoxSphereBounds ComponentBounds = Component->Bounds; Bounds = bFirst ? ComponentBounds : Bounds + ComponentBounds; bFirst = false; } return Bounds; }; // Compute texel density if needed, depending on the TextureSizingType setting ETextureSizingType TextureSizingType = UseSettings.MaterialSettings.TextureSizingType; float TexelDensityPerMeter = 0.0f; IGeometryProcessing_ApproximateActors::ETextureSizePolicy TextureSizePolicy = IGeometryProcessing_ApproximateActors::ETextureSizePolicy::TextureSize; if (TextureSizingType == ETextureSizingType::TextureSizingType_AutomaticFromTexelDensity) { TexelDensityPerMeter = UseSettings.MaterialSettings.TargetTexelDensityPerMeter; TextureSizePolicy = IGeometryProcessing_ApproximateActors::ETextureSizePolicy::TexelDensity; } else if (TextureSizingType == ETextureSizingType::TextureSizingType_AutomaticFromMeshScreenSize) { TexelDensityPerMeter = FMaterialUtilities::ComputeRequiredTexelDensityFromScreenSize(UseSettings.MaterialSettings.MeshMaxScreenSizePercent, GetActorsBounds().SphereRadius); TextureSizePolicy = IGeometryProcessing_ApproximateActors::ETextureSizePolicy::TexelDensity; } else if (TextureSizingType == ETextureSizingType::TextureSizingType_AutomaticFromMeshDrawDistance) { TexelDensityPerMeter = FMaterialUtilities::ComputeRequiredTexelDensityFromDrawDistance(UseSettings.MaterialSettings.MeshMinDrawDistance, GetActorsBounds().SphereRadius); TextureSizePolicy = IGeometryProcessing_ApproximateActors::ETextureSizePolicy::TexelDensity; } Options.MeshTexelDensity = TexelDensityPerMeter; Options.TextureSizePolicy = TextureSizePolicy; // run actor approximation computation IGeometryProcessing_ApproximateActors::FResults Results; ApproxActorsAPI->ApproximateActors(Actors.Array(), Options, Results); TArray Components; if (Results.ResultCode == IGeometryProcessing_ApproximateActors::EResultCode::Success) { auto FixupAsset = [&InHLODActor](UObject* Asset) { Asset->ClearFlags(RF_Public | RF_Standalone); Asset->Rename(nullptr, InHLODActor->GetPackage(), REN_NonTransactional | REN_DontCreateRedirectors | REN_ForceNoResetLoaders); }; Algo::ForEach(Results.NewMeshAssets, FixupAsset); Algo::ForEach(Results.NewMaterials, FixupAsset); Algo::ForEach(Results.NewTextures, FixupAsset); for (UStaticMesh* StaticMesh : Results.NewMeshAssets) { UStaticMeshComponent* Component = NewObject(InHLODActor); Component->SetStaticMesh(StaticMesh); DisableCollisions(Component); Components.Add(Component); } for (UMaterialInterface* Material : Results.NewMaterials) { UMaterialInstance* MaterialInst = CastChecked(Material); FStaticParameterSet StaticParameterSet; auto SetStaticSwitch = [&StaticParameterSet](FName ParamName, bool bSet) { if (bSet) { FStaticSwitchParameter SwitchParameter; SwitchParameter.ParameterInfo.Name = ParamName; SwitchParameter.Value = true; SwitchParameter.bOverride = true; StaticParameterSet.StaticSwitchParameters.Add(SwitchParameter); } }; // Set proper switches needed by our base flatten material SetStaticSwitch("UseBaseColor", Options.bBakeBaseColor); SetStaticSwitch("UseRoughness", Options.bBakeRoughness); SetStaticSwitch("UseMetallic", Options.bBakeMetallic); SetStaticSwitch("UseSpecular", Options.bBakeSpecular); SetStaticSwitch("UseEmissiveHDR", Options.bBakeEmissive); SetStaticSwitch("UseNormal", Options.bBakeNormalMap); SetStaticSwitch("PackMetallic", Options.bUsePackedMRS); SetStaticSwitch("PackSpecular", Options.bUsePackedMRS); SetStaticSwitch("PackRoughness", Options.bUsePackedMRS); // Force initializing the static permutations according to the switches we have set MaterialInst->UpdateStaticPermutation(StaticParameterSet); MaterialInst->InitStaticPermutation(); MaterialInst->PostEditChange(); } } // Batch instances if (InstancedComponents.Num()) { FHLODBuilder_Instancing InstancingHLODBuilder; Components.Append(InstancingHLODBuilder.CreateComponents(InHLODActor, nullptr, InstancedComponents)); } return Components; }