// Copyright Epic Games, Inc. All Rights Reserved. #include "WaterBodyHLODBuilder.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(WaterBodyHLODBuilder) #if WITH_EDITOR #include "Components/StaticMeshComponent.h" #include "UObject/Package.h" #include "WaterBodyComponent.h" #include "MeshDescription.h" #include "Engine/StaticMesh.h" #include "Materials/MaterialInterface.h" #include "MeshSimplification.h" #include "MeshDescriptionToDynamicMesh.h" #include "DynamicMeshToMeshDescription.h" #include "Serialization/ArchiveCrc32.h" #include "Engine/HLODProxy.h" #include "SceneView.h" #endif UWaterBodyHLODBuilder::UWaterBodyHLODBuilder(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } #if WITH_EDITOR static double GetPixelToWorldUnit(const double InDistanceZ) { // Generate a projection matrix. static const FIntPoint ScreenSize(1920, 1080); static const float HalfFOVRad = FMath::DegreesToRadians(45.0f); static const FMatrix InvProjectionMatrix = FPerspectiveMatrix(HalfFOVRad, ScreenSize.X, ScreenSize.Y, 0.01f).Inverse(); const FVector2D ScreenPixel((ScreenSize / 2) + FIntPoint(1, 0)); FVector Origin, Direction; FSceneView::DeprojectScreenToWorld(ScreenPixel, FIntRect(FIntPoint(0, 0), ScreenSize), InvProjectionMatrix, Origin, Direction); const FVector4d WorldPos = -Direction * InDistanceZ; return WorldPos.X; } uint32 UWaterBodyHLODBuilder::ComputeHLODHash(const UActorComponent* InSourceComponent) const { FArchiveCrc32 Ar; if (const UWaterBodyComponent* WaterBodyComponent = Cast(InSourceComponent)) { uint32 TransformHash = UHLODProxy::GetCRC(WaterBodyComponent->GetComponentTransform()); Ar << TransformHash; FMeshDescription HLODMesh = WaterBodyComponent->GetHLODMeshDescription(); Ar << HLODMesh; UMaterialInterface* HLODMaterial = WaterBodyComponent->GetHLODMaterial(); Ar << HLODMaterial; } return Ar.GetCrc(); } TArray UWaterBodyHLODBuilder::Build(const FHLODBuildContext& InHLODBuildContext, const TArray& InSourceComponents) const { TArray SourceWaterBodyComponents = FilterComponents(InSourceComponents); TArray HLODComponents; TArray StaticMeshes; // Compute the maximum error to keep displacement under a single pixel for the requested HLOD draw distance. const double MaxDisplacement = GetPixelToWorldUnit(InHLODBuildContext.MinVisibleDistance); const double MaxDisplacementSqr = FMath::Square(MaxDisplacement); for (UWaterBodyComponent* WaterBodyComponent : SourceWaterBodyComponents) { FName StaticMeshName = MakeUniqueObjectName(nullptr, UStaticMesh::StaticClass(), TEXT("WaterBodyHLODMesh")); UStaticMesh* StaticMesh = NewObject(InHLODBuildContext.AssetsOuter, StaticMeshName); // Mesh { FMeshDescription HLODMesh = WaterBodyComponent->GetHLODMeshDescription(); // Simplify mesh { // MeshDescription -> DynamicMesh UE::Geometry::FDynamicMesh3 DynamicMesh; FMeshDescriptionToDynamicMesh MeshDescriptionToDynamicMesh; MeshDescriptionToDynamicMesh.bDisableAttributes = true; MeshDescriptionToDynamicMesh.Convert(&HLODMesh, DynamicMesh, false); // Simplify UE::Geometry::FQEMSimplification Simplifier(&DynamicMesh); Simplifier.bPreserveBoundaryShape = false; Simplifier.SimplifyToMaxError(MaxDisplacementSqr); // DynamicMesh -> MeshDescription FMeshDescription SimplifiedWaterMesh = HLODMesh; FDynamicMeshToMeshDescription DynamicMeshToMeshDescription; DynamicMeshToMeshDescription.Convert(&DynamicMesh, SimplifiedWaterMesh); HLODMesh = SimplifiedWaterMesh; } StaticMesh->AddSourceModel(); StaticMesh->CreateMeshDescription(0, HLODMesh); StaticMesh->CommitMeshDescription(0); StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion; } // Material { UMaterialInterface* HLODMaterial = WaterBodyComponent->GetHLODMaterial(); StaticMesh->AddMaterial(HLODMaterial); } StaticMeshes.Add(StaticMesh); FName ComponentName = MakeUniqueObjectName(nullptr, UStaticMeshComponent::StaticClass(), TEXT("WaterHLODMeshComponent")); UStaticMeshComponent* StaticMeshComponent = NewObject(GetTransientPackage(), ComponentName); StaticMeshComponent->SetRelativeTransform(WaterBodyComponent->GetComponentTransform()); StaticMeshComponent->SetStaticMesh(StaticMesh); HLODComponents.Add(StaticMeshComponent); } UStaticMesh::BatchBuild(StaticMeshes); return HLODComponents; } #endif // #if WITH_EDITOR