Files
UnrealEngine/Engine/Source/Runtime/Landscape/Private/LandscapeEditLayerMergeContext.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

308 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LandscapeEditLayerMergeContext.h"
#include "Algo/AllOf.h"
#include "Algo/Transform.h"
#include "Landscape.h"
#include "LandscapeInfo.h"
#include "LandscapeLayerInfoObject.h"
#include "LandscapeEditLayerTargetLayerGroup.h"
#include "Materials/MaterialExpressionLandscapeVisibilityMask.h"
namespace UE::Landscape::EditLayers
{
#if WITH_EDITOR
FMergeContext::FMergeContext(ALandscape* InLandscape, bool bInIsHeightmapMerge, bool bInSkipProceduralRenderers)
: bIsHeightmapMerge(bInIsHeightmapMerge)
, bSkipProceduralRenderers(bInSkipProceduralRenderers)
, Landscape(InLandscape)
, LandscapeInfo(InLandscape->GetLandscapeInfo())
{
check(LandscapeInfo != nullptr);
// Start by gathering all possible target layer names on this landscape:
// This list of all unique target layer names will help accelerate the gathering of output layers on each component (using bit arrays) as well as the target layers intersection tests:
if (bInIsHeightmapMerge)
{
// Only one target layer in the case of heightmap:
const FName TargetLayerName = FName("Height");
AllTargetLayerNames = { TargetLayerName };
// And it's valid :
ValidTargetLayerBitIndices = TBitArray<>(true, 1);
VisibilityTargetLayerMask = TBitArray<>(false, 1);
AllWeightmapLayerInfos = { nullptr };
}
else
{
// Gather all target layer names and mark those that are valid layers :
for (const FLandscapeInfoLayerSettings& LayerSettings : LandscapeInfo->Layers)
{
check(!LayerSettings.LayerName.IsNone());
ULandscapeLayerInfoObject*& LayerInfo = AllWeightmapLayerInfos.Add_GetRef(LayerSettings.LayerInfoObj);
AllTargetLayerNames.Add(LayerSettings.LayerName);
ValidTargetLayerBitIndices.Add(LayerInfo != nullptr);
}
// Visibility is always a valid layer :
VisibilityTargetLayerIndex = AllTargetLayerNames.Find(UMaterialExpressionLandscapeVisibilityMask::ParameterName);
if (VisibilityTargetLayerIndex == INDEX_NONE)
{
VisibilityTargetLayerIndex = AllTargetLayerNames.Num();
AllTargetLayerNames.Add(UMaterialExpressionLandscapeVisibilityMask::ParameterName);
check(!AllWeightmapLayerInfos.Contains(ALandscape::VisibilityLayer));
AllWeightmapLayerInfos.Add(ALandscape::VisibilityLayer);
check(ALandscape::VisibilityLayer != nullptr);
ValidTargetLayerBitIndices.Add(true);
}
check(VisibilityTargetLayerIndex != INDEX_NONE);
// Now that we have all valid target layers ready, analyze them to prepare the target layer groups as they are declared within the layer infos :
FinalizeTargetLayerGroupsPerBlendingMethod();
VisibilityTargetLayerMask = TBitArray<>(false, AllTargetLayerNames.Num());
VisibilityTargetLayerMask[VisibilityTargetLayerIndex] = true;
}
NegatedVisibilityTargetLayerMask = VisibilityTargetLayerMask;
NegatedVisibilityTargetLayerMask.BitwiseNOT();
}
TArray<FName> FMergeContext::GetValidTargetLayerNames() const
{
return bIsHeightmapMerge ? AllTargetLayerNames : ConvertTargetLayerBitIndicesToNames(ValidTargetLayerBitIndices);
}
int32 FMergeContext::IsValidTargetLayerName(const FName& InName) const
{
int32 Index = GetTargetLayerIndexForName(InName);
return (Index != INDEX_NONE) ? ValidTargetLayerBitIndices[Index] : false;
}
int32 FMergeContext::IsValidTargetLayerNameChecked(const FName& InName) const
{
int32 Index = AllTargetLayerNames.Find(InName);
check(Index != INDEX_NONE);
return ValidTargetLayerBitIndices[Index];
}
int32 FMergeContext::IsTargetLayerIndexValid(int32 InIndex) const
{
return AllTargetLayerNames.IsValidIndex(InIndex);
}
int32 FMergeContext::GetTargetLayerIndexForName(const FName& InName) const
{
return AllTargetLayerNames.Find(InName);
}
int32 FMergeContext::GetTargetLayerIndexForNameChecked(const FName& InName) const
{
int32 Index = AllTargetLayerNames.Find(InName);
check(Index != INDEX_NONE);
return Index;
}
FName FMergeContext::GetTargetLayerNameForIndex(int32 InIndex) const
{
return AllTargetLayerNames.IsValidIndex(InIndex) ? AllTargetLayerNames[InIndex] : NAME_None;
}
FName FMergeContext::GetTargetLayerNameForIndexChecked(int32 InIndex) const
{
check(AllTargetLayerNames.IsValidIndex(InIndex));
return AllTargetLayerNames[InIndex];
}
int32 FMergeContext::GetTargetLayerIndexForLayerInfo(ULandscapeLayerInfoObject* InLayerInfo) const
{
return AllWeightmapLayerInfos.Find(InLayerInfo);
}
int32 FMergeContext::GetTargetLayerIndexForLayerInfoChecked(ULandscapeLayerInfoObject* InLayerInfo) const
{
int32 Index = AllWeightmapLayerInfos.Find(InLayerInfo);;
check(Index != INDEX_NONE);
return Index;
}
ULandscapeLayerInfoObject* FMergeContext::GetTargetLayerInfoForName(const FName& InName) const
{
int32 Index = GetTargetLayerIndexForName(InName);
return (Index != INDEX_NONE) ? AllWeightmapLayerInfos[Index] : nullptr;
}
ULandscapeLayerInfoObject* FMergeContext::GetTargetLayerInfoForNameChecked(const FName& InName) const
{
int32 Index = GetTargetLayerIndexForNameChecked(InName);
return AllWeightmapLayerInfos[Index];
}
ULandscapeLayerInfoObject* FMergeContext::GetTargetLayerInfoForIndex(int32 InIndex) const
{
check(AllTargetLayerNames.IsValidIndex(InIndex));
return AllWeightmapLayerInfos[InIndex];
}
TBitArray<> FMergeContext::ConvertTargetLayerNamesToBitIndices(TConstArrayView<FName> InTargetLayerNames) const
{
TBitArray<> Result(false, AllTargetLayerNames.Num());
for (FName Name : InTargetLayerNames)
{
if (int32 Index = GetTargetLayerIndexForName(Name); Index != INDEX_NONE)
{
Result[Index] = true;
}
}
return Result;
}
TBitArray<> FMergeContext::ConvertTargetLayerNamesToBitIndicesChecked(TConstArrayView<FName> InTargetLayerNames) const
{
TBitArray<> Result(false, AllTargetLayerNames.Num());
for (FName Name : InTargetLayerNames)
{
int32 Index = GetTargetLayerIndexForNameChecked(Name);
Result[Index] = true;
}
return Result;
}
TArray<FName> FMergeContext::ConvertTargetLayerBitIndicesToNames(const TBitArray<>& InTargetLayerBitIndices) const
{
const int32 NumNames = AllTargetLayerNames.Num();
check(InTargetLayerBitIndices.Num() == NumNames);
TArray<FName> Names;
Names.Reserve(NumNames);
for (TConstSetBitIterator It(InTargetLayerBitIndices); It; ++It)
{
Names.Add(AllTargetLayerNames[It.GetIndex()]);
}
return Names;
}
TArray<ULandscapeLayerInfoObject*> FMergeContext::ConvertTargetLayerBitIndicesToLayerInfos(const TBitArray<>& InTargetLayerBitIndices) const
{
const int32 NumTargetLayerInfos = AllTargetLayerNames.Num();
check(InTargetLayerBitIndices.Num() == NumTargetLayerInfos);
TArray<ULandscapeLayerInfoObject*> LayerInfos;
LayerInfos.Reserve(NumTargetLayerInfos);
for (TConstSetBitIterator It(InTargetLayerBitIndices); It; ++It)
{
LayerInfos.Add(AllWeightmapLayerInfos[It.GetIndex()]);
}
return LayerInfos;
}
void FMergeContext::ForEachTargetLayer(const TBitArray<>& InTargetLayerBitIndices, TFunctionRef<bool(int32 /*InTargetLayerIndex*/, const FName& /*InTargetLayerName*/, ULandscapeLayerInfoObject* /*InWeightmapLayerInfo*/)> Fn) const
{
check(InTargetLayerBitIndices.Num() == AllTargetLayerNames.Num());
for (TConstSetBitIterator It(InTargetLayerBitIndices); It; ++It)
{
const int32 TargetLayerIndex = It.GetIndex();
if (!AllTargetLayerNames.IsValidIndex(TargetLayerIndex))
{
return;
}
if (!Fn(TargetLayerIndex, AllTargetLayerNames[TargetLayerIndex], AllWeightmapLayerInfos[TargetLayerIndex]))
{
return;
}
}
}
void FMergeContext::ForEachValidTargetLayer(TFunctionRef<bool(int32 /*InTargetLayerIndex*/, const FName& /*InTargetLayerName*/, ULandscapeLayerInfoObject* /*InWeightmapLayerInfo*/)> Fn) const
{
return ForEachTargetLayer(ValidTargetLayerBitIndices, Fn);
}
TBitArray<> FMergeContext::BuildTargetLayerBitIndices(bool bInBitValue) const
{
return TBitArray<>(bInBitValue, AllTargetLayerNames.Num());
}
TArray<FTargetLayerGroup> FMergeContext::BuildGenericBlendTargetLayerGroups(const TBitArray<>& InTargetLayerBitIndices) const
{
// Only ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending is applied per renderer, so we only need to retrieve the target layer groups
// for the specified target layers and trim them of all other target layers
const TArray<FTargetLayerGroup>& SourceTargetLayerGroups = TargetLayerGroupsPerBlendingMethod[static_cast<int32>(ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending)];
// First, intersect our layer groups with the specified target layers
TArray<FTargetLayerGroup> FilteredTargetLayerGroups;
FilteredTargetLayerGroups.Reserve(SourceTargetLayerGroups.Num());
Algo::Transform(SourceTargetLayerGroups, FilteredTargetLayerGroups, [&InTargetLayerBitIndices](const FTargetLayerGroup& InTargetLayerGroup)
{
return FTargetLayerGroup(InTargetLayerGroup.GetName(), TBitArray<>::BitwiseAND(InTargetLayerGroup.GetWeightmapTargetLayerBitIndices(), InTargetLayerBitIndices, EBitwiseOperatorFlags::MinSize));
});
// Then remove all those that are now empty :
FilteredTargetLayerGroups.RemoveAllSwap([](const FTargetLayerGroup& InTargetLayerGroup) { return !InTargetLayerGroup.GetWeightmapTargetLayerBitIndices().Contains(true); });
return FilteredTargetLayerGroups;
}
void FMergeContext::FinalizeTargetLayerGroupsPerBlendingMethod()
{
for (int32 Index = 0; Index < static_cast<int32>(ELandscapeTargetLayerBlendMethod::Count); ++Index)
{
TargetLayerGroupsPerBlendingMethod[Index] = GatherTargetLayerGroupsForBlendMethod(static_cast<ELandscapeTargetLayerBlendMethod>(Index));
}
}
TArray<FTargetLayerGroup> FMergeContext::GatherTargetLayerGroupsForBlendMethod(ELandscapeTargetLayerBlendMethod InBlendMethod) const
{
TArray<FTargetLayerGroup> TargetLayerGroups;
switch (InBlendMethod)
{
case ELandscapeTargetLayerBlendMethod::None:
{
// Returning an empty layer group list for the no weight-blending case is fine because it will be treated as N independent layer groups (each with a single target layer)
}
break;
case ELandscapeTargetLayerBlendMethod::FinalWeightBlending:
{
TBitArray<> WeightBlendedWeightmapLayerBitIndices = BuildTargetLayerBitIndices(/*bInBitValue = */false);
ForEachValidTargetLayer(
[&WeightBlendedWeightmapLayerBitIndices](int32 InTargetLayerIndex, const FName& InTargetLayerName, ULandscapeLayerInfoObject* InWeightmapLayerInfo)
{
check(InWeightmapLayerInfo != nullptr);
WeightBlendedWeightmapLayerBitIndices[InTargetLayerIndex] = (InWeightmapLayerInfo->GetBlendMethod() == ELandscapeTargetLayerBlendMethod::FinalWeightBlending);
return true;
});
static const FName FinalWeightBlendingLayerGroupName(TEXT("FinalWeightBlendingLayerGroup"));
TargetLayerGroups.Emplace(FinalWeightBlendingLayerGroupName, WeightBlendedWeightmapLayerBitIndices);
}
break;
case ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending:
{
// Find all ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending layers and segregate them by blend group :
TMap<FName, TBitArray<>> TargetLayerGroupsMap;
const TBitArray<> DefaultTargetLayerBitIndices = BuildTargetLayerBitIndices(/*bInBitValue = */false);
ForEachValidTargetLayer(
[&TargetLayerGroupsMap, &DefaultTargetLayerBitIndices](int32 InTargetLayerIndex, const FName& InTargetLayerName, ULandscapeLayerInfoObject* InWeightmapLayerInfo)
{
check(InWeightmapLayerInfo != nullptr);
if (InWeightmapLayerInfo->GetBlendMethod() == ELandscapeTargetLayerBlendMethod::PremultipliedAlphaBlending)
{
TBitArray<>& TargetLayerGroup = TargetLayerGroupsMap.FindOrAdd(InWeightmapLayerInfo->GetBlendGroup(), DefaultTargetLayerBitIndices);
TargetLayerGroup[InTargetLayerIndex] = true;
}
return true;
});
Algo::Transform(TargetLayerGroupsMap, TargetLayerGroups, [](const TPair<FName, TBitArray<>>& InTargetLayerGroupPair) { return FTargetLayerGroup(InTargetLayerGroupPair.Key, InTargetLayerGroupPair.Value); });
}
break;
default:
check(false);
}
return TargetLayerGroups;
}
#endif // WITH_EDITOR
} // namespace UE::Landscape::EditLayers