Files
UnrealEngineUWP/Engine/Source/Runtime/Landscape/Private/LandscapeNaniteComponent.cpp
jonathan bard 08e482fca9 Added support for landscape material instances (per-component MICs) for Nanite landscape:
* Refactored ExportToRawMesh function to allow exporting one mesh section per component
* Implemented support for UV mappings usually provided by the landscape vertex factory in the non-Nanite case (as per the LandscapeLayerCoords material expression + others)
* For Nanite landscape materials, the relevant UV channels are :
** Texcoords0-2 : TerrainCoordMapping_XY, TerrainCoordMapping_XZ, TerrainCoordMapping_YZ (note : TerrainCoordMapping_XZ doesn't work ATM because texcoords1 seems to be a special case...)
** Texcoords3 : WeightmapUV
** Texcoords4 : LightmapUV (not implemented by ExportToRawMesh yet and not supported by Nanite meshes ATM : max 4 UV channels)
** Texcoords5 : HeightmapUV (implemented by ExportToRawMesh but not supported by Nanite meshes ATM : max 4 UV channels)
* Nanite landscape meshes now have 1 polygroup per component, with the proper landscape material instance being assigned to it
* Since Nanite meshes are capped at 64 meshes per section, Nanite landscape mesh will now fail to build on proxies sporting more than this amount of components. This is a first implementation and can be fixed later by adding as many Nanite landscape components as needed
* Implemented some missing virtual overrides on FLandscapeMaterialResource leading to landscape material instances potentially being used for non-landscape usages (hair, etc.)
* Fixed LandscapeNaniteComponent not being attached to the root component (and the mesh being exported in world space coordinates), leading to it not following when moving the  actor
* Added NaniteLODIndex property in ALandscape to be able to tweak the LOD level used when generating the Nanite meshes. It's mostly a debug feature to test the LODLevel > 0 landscape mesh export and to accelerate the Nanite landscape mesh generation since you usually want Nanite to be the most defined mesh possible
* All Nanite landscape meshes will be auto-invalidated by this change

#rb roey.borsteinas, graham.wihlidal
#preflight 6365e2a2882365b8590525ac
#lockdown marc.audy

[CL 23069843 by jonathan bard in ue5-main branch]
2022-11-09 21:04:18 -05:00

236 lines
9.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LandscapeNaniteComponent.h"
#include "LandscapeEdit.h"
#include "LandscapeRender.h"
#include "Materials/Material.h"
#include "NaniteSceneProxy.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Engine/StaticMesh.h"
#include "Engine/StaticMeshSourceData.h"
#include "NaniteDefinitions.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(LandscapeNaniteComponent)
#if WITH_EDITOR
#include "StaticMeshAttributes.h"
#include "StaticMeshDescription.h"
#include "StaticMeshOperations.h"
#include "MeshUtilitiesCommon.h"
#include "OverlappingCorners.h"
#include "MeshBuild.h"
#include "StaticMeshBuilder.h"
#include "NaniteBuilder.h"
#include "Rendering/NaniteResources.h"
#include "PhysicsEngine/BodySetup.h"
#include "StaticMeshCompiler.h"
#include "LandscapePrivate.h"
#endif
ULandscapeNaniteComponent::ULandscapeNaniteComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bEnabled(true)
{
}
void ULandscapeNaniteComponent::PostLoad()
{
Super::PostLoad();
ALandscapeProxy* LandscapeProxy = GetLandscapeProxy();
if (ensure(LandscapeProxy))
{
// Ensure that the component lighting and shadow settings matches the actor
UpdatedSharedPropertiesFromActor();
}
}
ALandscapeProxy* ULandscapeNaniteComponent::GetLandscapeProxy() const
{
return CastChecked<ALandscapeProxy>(GetOuter());
}
ALandscape* ULandscapeNaniteComponent::GetLandscapeActor() const
{
ALandscapeProxy* Landscape = GetLandscapeProxy();
if (Landscape)
{
return Landscape->GetLandscapeActor();
}
return nullptr;
}
void ULandscapeNaniteComponent::UpdatedSharedPropertiesFromActor()
{
ALandscapeProxy* LandscapeProxy = GetLandscapeProxy();
CastShadow = LandscapeProxy->CastShadow;
bCastDynamicShadow = LandscapeProxy->bCastDynamicShadow;
bCastStaticShadow = LandscapeProxy->bCastStaticShadow;
bCastContactShadow = LandscapeProxy->bCastContactShadow;
bCastFarShadow = LandscapeProxy->bCastFarShadow;
bCastHiddenShadow = LandscapeProxy->bCastHiddenShadow;
bCastShadowAsTwoSided = LandscapeProxy->bCastShadowAsTwoSided;
bAffectDistanceFieldLighting = LandscapeProxy->bAffectDistanceFieldLighting;
bRenderCustomDepth = LandscapeProxy->bRenderCustomDepth;
CustomDepthStencilWriteMask = LandscapeProxy->CustomDepthStencilWriteMask;
CustomDepthStencilValue = LandscapeProxy->CustomDepthStencilValue;
SetCullDistance(LandscapeProxy->LDMaxDrawDistance);
LightingChannels = LandscapeProxy->LightingChannels;
// We don't want Nanite representation in ray tracing
bVisibleInRayTracing = false;
// We don't want WPO evaluation enabled on landscape meshes
bEvaluateWorldPositionOffset = false;
}
void ULandscapeNaniteComponent::SetEnabled(bool bValue)
{
if (bValue != bEnabled)
{
bEnabled = bValue;
MarkRenderStateDirty();
}
}
#if WITH_EDITOR
bool ULandscapeNaniteComponent::InitializeForLandscape(ALandscapeProxy* Landscape, const FGuid& NewProxyContentId)
{
UStaticMesh* NaniteStaticMesh = NewObject<UStaticMesh>(this /* Outer */, TEXT("LandscapeNaniteMesh"), RF_Transactional);
FMeshDescription* MeshDescription = nullptr;
// Mesh
{
FStaticMeshSourceModel& SrcModel = NaniteStaticMesh->AddSourceModel();
// Don't allow the engine to recalculate normals
SrcModel.BuildSettings.bRecomputeNormals = false;
SrcModel.BuildSettings.bRecomputeTangents = false;
SrcModel.BuildSettings.bRemoveDegenerates = false;
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
FMeshNaniteSettings& NaniteSettings = NaniteStaticMesh->NaniteSettings;
NaniteSettings.bEnabled = true;
NaniteSettings.FallbackPercentTriangles = 0.01f; // Keep effectively no fallback mesh triangles
NaniteSettings.FallbackRelativeError = 1.0f;
const int32 LOD = Landscape->GetLandscapeActor()->NaniteLODIndex;
MeshDescription = NaniteStaticMesh->CreateMeshDescription(LOD);
{
TArray<UMaterialInterface*, TInlineAllocator<4>> InputMaterials;
TArray<FName, TInlineAllocator<4>> InputMaterialSlotNames;
TInlineComponentArray<ULandscapeComponent*> InputComponents;
for (ULandscapeComponent* Component : Landscape->LandscapeComponents)
{
UMaterialInterface* Material = nullptr;
if (Component)
{
Material = Component->GetMaterialInstance(LOD);
InputMaterialSlotNames.Add(FName(*FString::Format(TEXT("LandscapeMat_{0}"), { InputComponents.Num() })));
InputMaterials.Add(Material ? Material : UMaterial::GetDefaultMaterial(MD_Surface));
InputComponents.Add(Component);
}
}
if (InputComponents.Num() == 0)
{
UE_LOG(LogLandscape, Verbose, TEXT("%s : no Nanite mesh to export"), *GetOwner()->GetActorNameOrLabel());
return false;
}
if (InputMaterials.Num() >= NANITE_MAX_CLUSTER_MATERIALS)
{
UE_LOG(LogLandscape, Warning, TEXT("%s : Nanite landscape mesh would have more than %i materials, which is currently not supported. Please reduce the number of components in this landscape actor to enable Nanite."), *GetOwner()->GetActorNameOrLabel(), NANITE_MAX_CLUSTER_MATERIALS)
return false;
}
ALandscapeProxy::FRawMeshExportParams ExportParams;
ExportParams.ComponentsToExport = MakeArrayView(InputComponents.GetData(), InputComponents.Num());
ExportParams.ComponentsMaterialSlotName = MakeArrayView(InputMaterialSlotNames.GetData(), InputMaterialSlotNames.Num());
ExportParams.ExportLOD = LOD;
ExportParams.ExportCoordinatesType = ALandscapeProxy::FRawMeshExportParams::EExportCoordinatesType::RelativeToProxy;
ExportParams.UVConfiguration.ExportUVMappingTypes.SetNumZeroed(4);
ExportParams.UVConfiguration.ExportUVMappingTypes[0] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::TerrainCoordMapping_XY; // In LandscapeVertexFactory, Texcoords0 = ETerrainCoordMappingType::TCMT_XY (or ELandscapeCustomizedCoordType::LCCT_CustomUV0)
ExportParams.UVConfiguration.ExportUVMappingTypes[1] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::TerrainCoordMapping_XZ; // In LandscapeVertexFactory, Texcoords1 = ETerrainCoordMappingType::TCMT_XZ (or ELandscapeCustomizedCoordType::LCCT_CustomUV1)
ExportParams.UVConfiguration.ExportUVMappingTypes[2] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::TerrainCoordMapping_YZ; // In LandscapeVertexFactory, Texcoords2 = ETerrainCoordMappingType::TCMT_YZ (or ELandscapeCustomizedCoordType::LCCT_CustomUV2)
ExportParams.UVConfiguration.ExportUVMappingTypes[3] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::WeightmapUV; // In LandscapeVertexFactory, Texcoords3 = ELandscapeCustomizedCoordType::LCCT_WeightMapUV
// COMMENT [jonathan.bard] ATM Nanite meshes only support up to 4 UV sets so we cannot support those 2 :
//ExportParams.UVConfiguration.ExportUVMappingTypes[4] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::LightmapUV; // In LandscapeVertexFactory, Texcoords4 = lightmap UV
//ExportParams.UVConfiguration.ExportUVMappingTypes[5] = ALandscapeProxy::FRawMeshExportParams::EUVMappingType::HeightmapUV; // // In LandscapeVertexFactory, Texcoords5 = heightmap UV
bool bSuccess = Landscape->ExportToRawMesh(ExportParams, *MeshDescription);
const FPolygonGroupArray& PolygonGroups = MeshDescription->PolygonGroups();
checkf(bSuccess && (PolygonGroups.Num() == InputComponents.Num()), TEXT("Invalid landscape static mesh raw mesh export for actor %s (%i components)"), *GetOwner()->GetName(), InputComponents.Num());
check(InputMaterials.Num() == InputComponents.Num());
FStaticMeshAttributes MeshAttributes(*MeshDescription);
TPolygonGroupAttributesRef<FName> PolygonGroupMaterialSlotNames = MeshAttributes.GetPolygonGroupMaterialSlotNames();
int32 ComponentIndex = 0;
for (UMaterialInterface* Material : InputMaterials)
{
check(Material != nullptr);
const FName MaterialSlotName = InputMaterialSlotNames[ComponentIndex];
check(PolygonGroupMaterialSlotNames.GetRawArray().Contains(MaterialSlotName));
NaniteStaticMesh->GetStaticMaterials().Add(FStaticMaterial(Material, MaterialSlotName));
++ComponentIndex;
}
UE_LOG(LogLandscape, Verbose, TEXT("Successful export of raw static mesh for Nanite landscape (%i components) for actor %s"), InputComponents.Num(), *GetOwner()->GetName());
}
NaniteStaticMesh->CommitMeshDescription(0);
NaniteStaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
}
// Disable collisions
if (UBodySetup* BodySetup = NaniteStaticMesh->GetBodySetup())
{
BodySetup->DefaultInstance.SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
BodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
}
SetStaticMesh(NaniteStaticMesh);
UStaticMesh::BatchBuild({ NaniteStaticMesh });
ProxyContentId = NewProxyContentId;
return true;
}
bool ULandscapeNaniteComponent::InitializePlatformForLandscape(ALandscapeProxy* Landscape, const ITargetPlatform* TargetPlatform)
{
// This is a workaround. IsCachedCookedPlatformDataLoaded needs to return true to ensure that StreamablePages are loaded from DDC
if (TargetPlatform)
{
UStaticMesh* NaniteStaticMesh = GetStaticMesh();
NaniteStaticMesh->BeginCacheForCookedPlatformData(TargetPlatform);
FStaticMeshCompilingManager::Get().FinishCompilation({ NaniteStaticMesh });
const double StartTime = FPlatformTime::Seconds();
while (!NaniteStaticMesh->IsCachedCookedPlatformDataLoaded(TargetPlatform))
{
FAssetCompilingManager::Get().ProcessAsyncTasks(true);
FPlatformProcess::Sleep(0.01);
constexpr double MaxWaitSeconds = 240.0;
if (FPlatformTime::Seconds() - StartTime > MaxWaitSeconds)
{
UE_LOG(LogLandscape, Error, TEXT("ULandscapeNaniteComponent::InitializePlatformForLandscape waited more than %f seconds for IsCachedCookedPlatformDataLoaded to return true"), MaxWaitSeconds);
return false;
}
}
}
return true;
}
#endif