You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Currently, Landscape will only apply the new storage provider to it's heightmaps at cook time, when it sees that the landscape has been flagged to use the new compressed storage path (enabled under Landscape -> Advanced), and only for Windows platforms (to be expanded as we test other platforms).
The compressed storage path option on the landscape is hidden by default, and can be made visible via CVAR ("landscape.ShowCompressHeightMapsOption")
Makes use of a new All Mip Provider interface to Texture2Ds, that expands on the existing Mip Data Provider (that only supported providing streaming mips).
The new interface assumes you want to completely override the Texture2D mip data.
When Texture2D sees that it has an All Mip Provider, it will skip serializing it's own heavyweight mip data during cook, as it assumes the provider will take care of providing all mip data.
The majority of the implementation is in LandscapeTextureStorageProvider, which implements the AllMipProvider that can store landscape heightmaps in a custom compressed format. Each mip's height data is stored using delta encoding, and normals are reconstructed.
#rb jonathan.bard, jian.ru
#jira UE-180654
#preflight 64541acf6c35ad81e61890cc
#preflight 64542713fd4b8f4e0d9a2ce9
#preflight 6459378bfd4b8f4e0dc206d2
[CL 25381191 by chris tchou in ue5-main branch]
398 lines
14 KiB
C++
398 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LandscapeProxyUIDetails.h"
|
|
|
|
#include "Algo/AnyOf.h"
|
|
#include "Algo/Count.h"
|
|
#include "Algo/Find.h"
|
|
#include "Algo/Transform.h"
|
|
#include "Components/RuntimeVirtualTextureComponent.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/Map.h"
|
|
#include "DetailCategoryBuilder.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "Engine/World.h"
|
|
#include "Fonts/SlateFontInfo.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "IDetailPropertyRow.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "Landscape.h"
|
|
#include "LandscapeInfo.h"
|
|
#include "LandscapeProxy.h"
|
|
#include "LandscapeStreamingProxy.h"
|
|
#include "LandscapeSubsystem.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Math/IntPoint.h"
|
|
#include "Math/IntRect.h"
|
|
#include "Math/UnrealMathSSE.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "RuntimeVirtualTextureSetBounds.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Templates/Casts.h"
|
|
#include "Types/SlateEnums.h"
|
|
#include "UObject/LazyObjectPtr.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UObject/WeakObjectPtr.h"
|
|
#include "UObject/WeakObjectPtrTemplates.h"
|
|
#include "VT/RuntimeVirtualTextureVolume.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
|
|
class IPropertyHandle;
|
|
class SWidget;
|
|
class UObject;
|
|
class URuntimeVirtualTexture;
|
|
|
|
#define LOCTEXT_NAMESPACE "FLandscapeProxyUIDetails"
|
|
|
|
static TAutoConsoleVariable<int32> CVarShowCompressWeightMapsOption(
|
|
TEXT("landscape.ShowCompressHeightMapsOption"),
|
|
0,
|
|
TEXT("Enable editing of the compressed height map option on landscape proxies (experimental)."),
|
|
ECVF_Default);
|
|
|
|
FLandscapeProxyUIDetails::FLandscapeProxyUIDetails()
|
|
{
|
|
}
|
|
|
|
TSharedRef<IDetailCustomization> FLandscapeProxyUIDetails::MakeInstance()
|
|
{
|
|
return MakeShareable( new FLandscapeProxyUIDetails);
|
|
}
|
|
|
|
void FLandscapeProxyUIDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
|
|
{
|
|
TArray<TWeakObjectPtr<UObject>> EditingObjects;
|
|
DetailBuilder.GetObjectsBeingCustomized(EditingObjects);
|
|
|
|
auto GenerateTextWidget = [](const FText& InText, bool bInBold = false) -> TSharedRef<SWidget>
|
|
{
|
|
return SNew(STextBlock)
|
|
.Font(bInBold? IDetailLayoutBuilder::GetDetailFontBold() : IDetailLayoutBuilder::GetDetailFont())
|
|
.Text(InText);
|
|
};
|
|
|
|
TArray<TWeakObjectPtr<ALandscapeProxy>> EditingProxies;
|
|
Algo::Transform(EditingObjects, EditingProxies, [](TWeakObjectPtr<UObject> InObject) { return TWeakObjectPtr<ALandscapeProxy>(Cast<ALandscapeProxy>(InObject.Get())); });
|
|
|
|
TArray<TWeakObjectPtr<ALandscape>> LandscapeActors;
|
|
for (TWeakObjectPtr<ALandscapeProxy> EditingProxy : EditingProxies)
|
|
{
|
|
if (EditingProxy.IsValid())
|
|
{
|
|
if (ALandscape* LandscapeActor = EditingProxy->GetLandscapeActor())
|
|
{
|
|
LandscapeActors.AddUnique(LandscapeActor);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hide World Partition specific properties in non WP levels
|
|
const bool bShouldDisplayWorldPartitionProperties = Algo::AnyOf(EditingProxies, [](const TWeakObjectPtr<ALandscapeProxy> InProxy)
|
|
{
|
|
UWorld* World = InProxy.IsValid() ? InProxy->GetTypedOuter<UWorld>() : nullptr;
|
|
return UWorld::IsPartitionedWorld(World);
|
|
});
|
|
|
|
if (!bShouldDisplayWorldPartitionProperties)
|
|
{
|
|
DetailBuilder.HideProperty(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ALandscape, bAreNewLandscapeActorsSpatiallyLoaded), ALandscape::StaticClass()));
|
|
}
|
|
|
|
if (CVarShowCompressWeightMapsOption->GetInt() == 0)
|
|
{
|
|
DetailBuilder.HideProperty(DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ALandscapeProxy, bUseCompressedHeightmapStorage), ALandscapeProxy::StaticClass()));
|
|
}
|
|
|
|
if (LandscapeActors.Num() == 1)
|
|
{
|
|
TWeakObjectPtr<ALandscape> LandscapeActor = LandscapeActors[0];
|
|
if (ULandscapeInfo* LandscapeInfo = LandscapeActor->GetLandscapeInfo())
|
|
{
|
|
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory("Information", FText::GetEmpty(), ECategoryPriority::Important);
|
|
|
|
FText CreateRVTVolumesTooltip = LOCTEXT("Button_CreateVolumes_Tooltip", "Create volumes for the selected Runtime Virtual Textures.");
|
|
|
|
FText RowDisplayText = LOCTEXT("LandscapeComponentResolution", "Component Resolution (Verts)");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeComponentResolution"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeComponentResolutionValue", "{0} x {0}"), LandscapeActor->ComponentSizeQuads+1), true) // Verts
|
|
];
|
|
|
|
RowDisplayText = LOCTEXT("LandscapeComponentCount", "Component Count");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeComponentCount"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeComponentCountValue", "{0}"), LandscapeActor->LandscapeComponents.Num()), true)
|
|
];
|
|
|
|
RowDisplayText = LOCTEXT("LandscapeComponentSubsections", "Component Subsections");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeComponentSubsections"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeComponentSubSectionsValue", "{0} x {0}"), LandscapeActor->NumSubsections), true)
|
|
];
|
|
|
|
FIntRect Rect = LandscapeActor->GetBoundingRect();
|
|
FIntPoint Size = Rect.Size();
|
|
RowDisplayText = LOCTEXT("LandscapeResolution", "Resolution (Verts)");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeResolution"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeResolutionValue", "{0} x {1}"), Size.X+1, Size.Y+1), true)
|
|
];
|
|
|
|
int32 LandscapeCount = LandscapeInfo->StreamingProxies.Num() + (LandscapeInfo->LandscapeActor.Get() ? 1 : 0);
|
|
RowDisplayText = LOCTEXT("LandscapeCount", "Landscape Count");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeCount"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeCountValue", "{0}"), LandscapeCount), true)
|
|
];
|
|
|
|
int32 TotalComponentCount = LandscapeInfo->XYtoComponentMap.Num();
|
|
RowDisplayText = LOCTEXT("TotalLandscapeComponentCount", "Total Component Count");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("TotalLandscapeComponentCount"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("TotalLandscapeComponentCountValue", "{0}"), TotalComponentCount), true)
|
|
];
|
|
|
|
LandscapeInfo->GetLandscapeExtent(Rect.Min.X, Rect.Min.Y, Rect.Max.X, Rect.Max.Y);
|
|
Size = Rect.Size();
|
|
RowDisplayText = LOCTEXT("LandscapeOverallResolution", "Overall Resolution (Verts)");
|
|
CategoryBuilder.AddCustomRow(RowDisplayText)
|
|
.RowTag(TEXT("LandscapeOverallResolution"))
|
|
.NameContent()
|
|
[
|
|
GenerateTextWidget(RowDisplayText)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
GenerateTextWidget(FText::Format(LOCTEXT("LandscapeOverallResolutionValue", "{0} x {1}"), Size.X + 1, Size.Y + 1), true)
|
|
];
|
|
|
|
// Apply custom widget for CreateVolume.
|
|
TSharedRef<IPropertyHandle> CreateVolumePropertyHandle = DetailBuilder.GetProperty(TEXT("bSetCreateRuntimeVirtualTextureVolumes"));
|
|
DetailBuilder.EditDefaultProperty(CreateVolumePropertyHandle)->CustomWidget()
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.Text(LOCTEXT("Button_CreateVolumes", "Create Volumes"))
|
|
.ToolTipText(CreateRVTVolumesTooltip)
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(125.f)
|
|
[
|
|
SNew(SButton)
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
.ContentPadding(2)
|
|
.Text(LOCTEXT("Button_CreateVolumes", "Create Volumes"))
|
|
.OnClicked_Lambda([LandscapeActor, this]() { return CreateRuntimeVirtualTextureVolume(LandscapeActor.Get()); })
|
|
.IsEnabled_Lambda([LandscapeActor, this]() { return IsCreateRuntimeVirtualTextureVolumeEnabled(LandscapeActor.Get()); })
|
|
.ToolTipText_Lambda([LandscapeActor, this, CreateRVTVolumesTooltip]()
|
|
{
|
|
return IsCreateRuntimeVirtualTextureVolumeEnabled(LandscapeActor.Get())
|
|
? CreateRVTVolumesTooltip
|
|
: LOCTEXT("Button_CreateVolumes_Tooltip_Invalid", "No Runtime Virtual Textures found in the world.");
|
|
})
|
|
];
|
|
}
|
|
}
|
|
|
|
// Add Nanite buttons :
|
|
if (!EditingProxies.IsEmpty())
|
|
{
|
|
auto BuildNaniteData = [EditingProxies](bool bInForceRebuild) -> FReply
|
|
{
|
|
TArray<ALandscapeProxy*> ProxiesToBuild;
|
|
Algo::TransformIf(EditingProxies, ProxiesToBuild, [](const TWeakObjectPtr<ALandscapeProxy>& InProxy) { return InProxy.IsValid(); }, [](const TWeakObjectPtr<ALandscapeProxy>& InProxy) { return InProxy.Get(); });
|
|
if (ProxiesToBuild.IsEmpty())
|
|
{
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
if (UWorld* World = ProxiesToBuild[0]->GetWorld())
|
|
{
|
|
if (ULandscapeSubsystem* LandscapeSubsystem = World->GetSubsystem<ULandscapeSubsystem>())
|
|
{
|
|
LandscapeSubsystem->BuildNanite(MakeArrayView(ProxiesToBuild), bInForceRebuild);
|
|
}
|
|
}
|
|
|
|
return FReply::Handled();
|
|
};
|
|
|
|
auto GetNumProxiesNeedingRebuild = [EditingProxies](bool bInForceRebuild) -> int32
|
|
{
|
|
TSet<TWeakObjectPtr<ALandscapeProxy>> ProxiesToBuild;
|
|
for (TWeakObjectPtr<ALandscapeProxy> EditingProxy : EditingProxies)
|
|
{
|
|
if (EditingProxy.IsValid())
|
|
{
|
|
ProxiesToBuild.Add(EditingProxy);
|
|
// Build all streaming proxies in the case of a ALandscape :
|
|
if (ALandscape* Landscape = Cast<ALandscape>(EditingProxy.Get()))
|
|
{
|
|
ULandscapeInfo* LandscapeInfo = Landscape->GetLandscapeInfo();
|
|
if (LandscapeInfo != nullptr)
|
|
{
|
|
Algo::Transform(LandscapeInfo->StreamingProxies, ProxiesToBuild, [](const TWeakObjectPtr<ALandscapeStreamingProxy>& InStreamingProxy) { return InStreamingProxy; });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Algo::CountIf(ProxiesToBuild, [bInForceRebuild](TWeakObjectPtr<ALandscapeProxy> InProxy) { return (InProxy.IsValid() && (bInForceRebuild || !InProxy->IsNaniteMeshUpToDate())); });
|
|
};
|
|
|
|
auto HasAtLeastOneNaniteLandscape = [LandscapeActors]() -> bool
|
|
{
|
|
return Algo::FindByPredicate(LandscapeActors, [](TWeakObjectPtr<ALandscape> InLandscape) { return (InLandscape.IsValid() && InLandscape->IsNaniteEnabled()); }) != nullptr;
|
|
};
|
|
|
|
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory("Nanite", FText::GetEmpty(), ECategoryPriority::Default);
|
|
CategoryBuilder.AddCustomRow(FText::FromString(TEXT("Rebuild Nanite Data")))
|
|
.RowTag("RebuildNaniteData")
|
|
[
|
|
SNew(SHorizontalBox)
|
|
.IsEnabled_Lambda([HasAtLeastOneNaniteLandscape] { return HasAtLeastOneNaniteLandscape(); })
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SButton)
|
|
.Text(LOCTEXT("BuildNaniteData", "Build Data"))
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.ToolTipText_Lambda([GetNumProxiesNeedingRebuild]()
|
|
{
|
|
return FText::Format(LOCTEXT("BuildNaniteDataTooltip", "Builds the Nanite mesh representation from the Landscape data if it's not up to date ({0} {0}|plural(one=actors, other=actors) to build)"), GetNumProxiesNeedingRebuild(/*bInForceRebuild = */false));
|
|
})
|
|
.OnClicked_Lambda([BuildNaniteData]() { return BuildNaniteData(/*bInForceRebuild = */false); })
|
|
.IsEnabled_Lambda([GetNumProxiesNeedingRebuild]() { return (GetNumProxiesNeedingRebuild(/*bInForceRebuild = */false) > 0); })
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SButton)
|
|
.Text(LOCTEXT("RebuildNaniteData", "Rebuild Data"))
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.ToolTipText_Lambda([GetNumProxiesNeedingRebuild]()
|
|
{
|
|
return FText::Format(LOCTEXT("RebuildNaniteDataTooltip", "Rebuilds the Nanite mesh representation from the Landscape data ({0} {0}|plural(one=actors, other=actors) to build)"), GetNumProxiesNeedingRebuild(/*bInForceRebuild = */true));
|
|
})
|
|
.OnClicked_Lambda([BuildNaniteData]() { return BuildNaniteData(/*bInForceRebuild = */true); })
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
static void GetMissingRuntimeVirtualTextureVolumes(ALandscape* InLandscapeActor, TArray<URuntimeVirtualTexture*>& OutVirtualTextures)
|
|
{
|
|
UWorld* World = InLandscapeActor != nullptr ? InLandscapeActor->GetWorld() : nullptr;
|
|
if (World == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<URuntimeVirtualTexture*> FoundVolumes;
|
|
for (TObjectIterator<URuntimeVirtualTextureComponent> It(RF_ClassDefaultObject, false, EInternalObjectFlags::Garbage); It; ++It)
|
|
{
|
|
if (It->GetWorld() == World)
|
|
{
|
|
if (URuntimeVirtualTexture* VirtualTexture = It->GetVirtualTexture())
|
|
{
|
|
FoundVolumes.Add(VirtualTexture);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (URuntimeVirtualTexture* VirtualTexture : InLandscapeActor->RuntimeVirtualTextures)
|
|
{
|
|
if (VirtualTexture != nullptr && FoundVolumes.Find(VirtualTexture) == INDEX_NONE)
|
|
{
|
|
OutVirtualTextures.Add(VirtualTexture);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FLandscapeProxyUIDetails::IsCreateRuntimeVirtualTextureVolumeEnabled(ALandscape* InLandscapeActor) const
|
|
{
|
|
if (InLandscapeActor == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TArray<URuntimeVirtualTexture*> VirtualTextureVolumesToCreate;
|
|
GetMissingRuntimeVirtualTextureVolumes(InLandscapeActor, VirtualTextureVolumesToCreate);
|
|
return VirtualTextureVolumesToCreate.Num() > 0;
|
|
}
|
|
|
|
FReply FLandscapeProxyUIDetails::CreateRuntimeVirtualTextureVolume(ALandscape* InLandscapeActor)
|
|
{
|
|
if (InLandscapeActor == nullptr)
|
|
{
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
TArray<URuntimeVirtualTexture*> VirtualTextureVolumesToCreate;
|
|
GetMissingRuntimeVirtualTextureVolumes(InLandscapeActor, VirtualTextureVolumesToCreate);
|
|
if (VirtualTextureVolumesToCreate.Num() == 0)
|
|
{
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("Transaction_CreateVolumes", "Create Runtime Virtual Texture Volumes"));
|
|
|
|
for (URuntimeVirtualTexture* VirtualTexture : VirtualTextureVolumesToCreate)
|
|
{
|
|
ARuntimeVirtualTextureVolume* NewVolume = InLandscapeActor->GetWorld()->SpawnActor<ARuntimeVirtualTextureVolume>();
|
|
NewVolume->VirtualTextureComponent->SetVirtualTexture(VirtualTexture);
|
|
NewVolume->VirtualTextureComponent->SetBoundsAlignActor(InLandscapeActor);
|
|
RuntimeVirtualTexture::SetBounds(NewVolume->VirtualTextureComponent);
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|