Files
UnrealEngine/Engine/Source/Runtime/Landscape/Public/LandscapeEdit.h
2025-05-18 13:04:45 +08:00

652 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LandscapeEdit.h: Classes for the editor to access to Landscape data
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "RHI.h"
#include "LandscapeProxy.h"
#include "Engine/Texture2D.h"
#include "AI/NavigationSystemBase.h"
#include "Components/ActorComponent.h"
#include "LandscapeInfo.h"
#include "LandscapeComponent.h"
#include "LandscapeEditTypes.h"
#include "LandscapeHeightfieldCollisionComponent.h"
#include "InstancedFoliageActor.h"
#include "LandscapeLayerInfoObject.h"
#if WITH_EDITOR
#include "Containers/ArrayView.h"
#include "LandscapeTextureHash.h"
#endif
#include "Landscape.h"
class ULandscapeComponent;
class ULandscapeInfo;
class ULandscapeLayerInfoObject;
#define MAX_LANDSCAPE_LOD_DISTANCE_FACTOR UE_DEPRECATED_MACRO(5.6, "MAX_LANDSCAPE_LOD_DISTANCE_FACTOR has been deprecated because it is no longer necessary") 10.f
#if WITH_EDITOR
/** Landscape EdMode Interface (used by ALandscape in Editor mode to access EdMode properties) */
class ILandscapeEdModeInterface
{
public:
virtual void PostUpdateLayerContent() = 0;
virtual ELandscapeToolTargetType GetLandscapeToolTargetType() const = 0;
virtual const ULandscapeEditLayerBase* GetLandscapeSelectedLayer() const = 0;
virtual ULandscapeLayerInfoObject* GetSelectedLandscapeLayerInfo() const = 0;
virtual void OnCanHaveLayersContentChanged() = 0;
};
struct FLandscapeTextureDataInfo
{
struct FMipInfo
{
void* MipData;
TArray<FUpdateTextureRegion2D> MipUpdateRegions;
bool bFull;
};
FLandscapeTextureDataInfo(UTexture2D* InTexture, bool bShouldDirtyPackage, ELandscapeTextureUsage TextureUsage = ELandscapeTextureUsage::Unknown, ELandscapeTextureType TextureType = ELandscapeTextureType::Unknown);
virtual ~FLandscapeTextureDataInfo();
// returns true if we need to block on the render thread before unlocking the mip data
bool UpdateTextureData();
int32 NumMips() { return MipInfo.Num(); }
void AddMipUpdateRegion(int32 MipNum, int32 InX1, int32 InY1, int32 InX2, int32 InY2)
{
if (MipInfo[MipNum].bFull)
{
return;
}
check(MipNum < MipInfo.Num());
uint32 Width = 1 + InX2 - InX1;
uint32 Height = 1 + InY2 - InY1;
// Catch situation where we are updating the whole texture to avoid adding redundant regions once the whole region as been included.
if (Width == GetMipSizeX(MipNum) && Height == GetMipSizeY(MipNum))
{
MipInfo[MipNum].bFull = true;
MipInfo[MipNum].MipUpdateRegions.Reset();
// Push a full region for UpdateTextureData() to process later
MipInfo[MipNum].MipUpdateRegions.Emplace(0, 0, 0, 0, Width, Height);
return;
}
MipInfo[MipNum].MipUpdateRegions.Emplace(InX1, InY1, InX1, InY1, Width, Height);
}
void* GetMipData(int32 MipNum)
{
check( MipNum < MipInfo.Num() );
if( !MipInfo[MipNum].MipData )
{
// will Unlock in destructor
MipInfo[MipNum].MipData = Texture->Source.LockMip(MipNum);
// probably should check that we got the lock :
//check( MipInfo[MipNum].MipData != nullptr );
// also this return value is usually cast to FColor *
// should instead have a function that returns FColor * and ensures the mip actually is BGRA8
}
return MipInfo[MipNum].MipData;
}
int32 GetMipSizeX(int32 MipNum) const
{
return FMath::Max(Texture->Source.GetSizeX() >> MipNum, 1);
}
int32 GetMipSizeY(int32 MipNum) const
{
return FMath::Max(Texture->Source.GetSizeY() >> MipNum, 1);
}
private:
UTexture2D* Texture;
TArray<FMipInfo> MipInfo;
ELandscapeTextureUsage TextureUsage = ELandscapeTextureUsage::Unknown;
ELandscapeTextureType TextureType = ELandscapeTextureType::Unknown;
};
struct FLandscapeTextureDataInterface
{
LANDSCAPE_API FLandscapeTextureDataInterface(bool bInUploadTextureChangesToGPU = true);
LANDSCAPE_API virtual ~FLandscapeTextureDataInterface();
void SetShouldDirtyPackage(bool bValue) { bShouldDirtyPackage = bValue; }
bool GetShouldDirtyPackage() const { return bShouldDirtyPackage; }
// Texture data access
LANDSCAPE_API FLandscapeTextureDataInfo* GetTextureDataInfo(UTexture2D* Texture);
// Flush texture updates
LANDSCAPE_API void Flush();
// Texture bulk operations for weightmap reallocation
LANDSCAPE_API void CopyTextureChannel(UTexture2D* Dest, int32 DestChannel, UTexture2D* Src, int32 SrcChannel);
LANDSCAPE_API void ZeroTextureChannel(UTexture2D* Dest, int32 DestChannel);
LANDSCAPE_API void CopyTextureFromHeightmap(UTexture2D* Dest, ULandscapeComponent* Comp, int32 MipIndex);
LANDSCAPE_API void CopyTextureFromHeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, int32 SrcChannel);
LANDSCAPE_API void CopyTextureFromWeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo);
LANDSCAPE_API void CopyTextureFromWeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo, int32 MipIndex);
template<typename TData>
void SetTextureValueTempl(UTexture2D* Dest, TData Value);
LANDSCAPE_API void ZeroTexture(UTexture2D* Dest);
LANDSCAPE_API void SetTextureValue(UTexture2D* Dest, FColor Value);
template<typename TData>
bool EqualTextureValueTempl(UTexture2D* Src, TData Value);
LANDSCAPE_API bool EqualTextureValue(UTexture2D* Src, FColor Value);
private:
LANDSCAPE_API void CopyTextureFromWeightmap(FLandscapeTextureDataInfo* DestDataInfo, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo, int32 MipIndex);
TMap<UTexture2D*, FLandscapeTextureDataInfo*> TextureDataMap;
bool bUploadTextureChangesToGPU;
bool bShouldDirtyPackage;
};
struct FLandscapeEditDataInterface : public FLandscapeTextureDataInterface
{
// this constructor will build an interface that works in the current edit layer (uses the ALandscape PrivateEditingLayer state)
LANDSCAPE_API FLandscapeEditDataInterface(ULandscapeInfo* InLandscape, bool bInUploadTextureChangesToGPU = true);
// this constructor will build an interface that works in the specified edit layer : InEditLayerGUID
LANDSCAPE_API FLandscapeEditDataInterface(ULandscapeInfo* InLandscape, const FGuid& InEditLayerGUID, bool bInUploadTextureChangesToGPU = true);
// switch the layer this interface works in
LANDSCAPE_API void SetEditLayer(const FGuid& InEditLayerGUID);
// returns the current layer this interface is working in
LANDSCAPE_API FGuid GetEditLayer() const;
// Misc
LANDSCAPE_API bool GetComponentsInRegion(int32 X1, int32 Y1, int32 X2, int32 Y2, TSet<ULandscapeComponent*>* OutComponents = NULL);
//
// Heightmap access
//
LANDSCAPE_API void SetHeightData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint16* InData, int32 InStride, bool InCalcNormals, const uint16* InNormalData = nullptr, const uint16* InHeightAlphaBlendData = nullptr, const uint8* InHeightRaiseLowerData = nullptr, bool InCreateComponents = false, UTexture2D* InHeightmap = nullptr, UTexture2D* InXYOffsetmapTexture = nullptr,
bool InUpdateBounds = true, bool InUpdateCollision = true, bool InGenerateMips = true);
// Helper accessor
LANDSCAPE_API FORCEINLINE uint16 GetHeightMapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData = NULL);
// Helper accessor
LANDSCAPE_API FORCEINLINE uint16 GetHeightMapAlphaBlendData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData = NULL);
// Helper accessor
LANDSCAPE_API FORCEINLINE uint8 GetHeightMapFlagsData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData = NULL);
// Generic
template<typename TStoreData>
void GetHeightDataTempl(int32& X1, int32& Y1, int32& X2, int32& Y2, TStoreData& StoreData);
template<typename TStoreData>
void GetHeightAlphaBlendDataTempl(int32& X1, int32& Y1, int32& X2, int32& Y2, TStoreData& StoreData);
template<typename TStoreData>
void GetHeightFlagsDataTempl(int32& X1, int32& Y1, int32& X2, int32& Y2, TStoreData& StoreData);
// Without data interpolation, able to get normal data
template<typename TStoreData>
void GetHeightDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData, UTexture2D* InHeightmap = nullptr, TStoreData* NormalData = NULL);
// Implementation for fixed array
LANDSCAPE_API void GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint16* Data, int32 Stride);
LANDSCAPE_API void GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint16* Data, int32 Stride, uint16* NormalData = NULL, UTexture2D* InHeightmap = nullptr);
LANDSCAPE_API void GetHeightAlphaBlendData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint16* Data, int32 Stride);
LANDSCAPE_API void GetHeightFlagsData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint8* Data, int32 Stride);
// Implementation for sparse array
LANDSCAPE_API void GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint16>& SparseData);
LANDSCAPE_API void GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint16>& SparseData, TMap<FIntPoint, uint16>* NormalData = NULL, UTexture2D* InHeightmap = nullptr);
// Recaclulate normals for the entire landscape.
LANDSCAPE_API void RecalculateNormals();
//
// Weightmap access
//
// Helper accessor
LANDSCAPE_API FORCEINLINE uint8 GetWeightMapData(const ULandscapeComponent* Component, ULandscapeLayerInfoObject* LayerInfo, int32 TexU, int32 TexV, uint8 Offset = 0, UTexture2D* Texture = NULL, uint8* TextureData = NULL);
template<typename TStoreData>
void GetWeightDataTempl(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, TStoreData& StoreData);
// Without data interpolation
template<typename TStoreData>
void GetWeightDataTemplFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData);
// Implementation for fixed array
LANDSCAPE_API void GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, uint8* Data, int32 Stride);
//void GetWeightData(FName LayerName, int32& X1, int32& Y1, int32& X2, int32& Y2, TArray<uint8>* Data, int32 Stride);
LANDSCAPE_API void GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride);
LANDSCAPE_API void GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TArray<uint8>* Data, int32 Stride);
// Implementation for sparse array
LANDSCAPE_API void GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint8>& SparseData);
//void GetWeightData(FName LayerName, int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<uint64, TArray<uint8>>& SparseData);
LANDSCAPE_API void GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData);
LANDSCAPE_API void GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, TArray<uint8>>& SparseData);
// Updates weightmap for LayerInfo, optionally adjusting all other weightmaps.
LANDSCAPE_API void SetAlphaData(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction PaintingRestriction = ELandscapeLayerPaintingRestriction::None, bool bWeightAdjust = true, bool bTotalWeightAdjust = false);
// Updates weightmaps for all layers. Data points to packed data for all layers in the landscape info
LANDSCAPE_API void SetAlphaData(const TSet<ULandscapeLayerInfoObject*>& DirtyLayerInfos, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction PaintingRestriction = ELandscapeLayerPaintingRestriction::None);
// Delete a layer and re-normalize other layers
LANDSCAPE_API void DeleteLayer(ULandscapeLayerInfoObject* LayerInfo);
// Fill a layer and re-normalize other layers
LANDSCAPE_API void FillLayer(ULandscapeLayerInfoObject* LayerInfo);
// Fill all empty layers and re-normalize layers
LANDSCAPE_API void FillEmptyLayers(ULandscapeLayerInfoObject* LayerInfo);
// Replace/merge a layer (across all edit layers and the main runtime layer)
LANDSCAPE_API void ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo);
template<typename TStoreData>
void GetEditToolTextureData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData, TFunctionRef<UTexture2D*(ULandscapeComponent*)> GetComponentTexture);
LANDSCAPE_API void SetEditToolTextureData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride, TFunctionRef<UTexture2D*&(ULandscapeComponent*)> GetComponentTexture, TextureGroup InTextureGroup = TEXTUREGROUP_Terrain_Weightmap);
// Without data interpolation, Select Data
template<typename TStoreData>
void GetSelectDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData);
LANDSCAPE_API void GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride);
LANDSCAPE_API void GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData);
LANDSCAPE_API void SetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride);
template<typename TStoreData>
void GetLayerContributionDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData);
LANDSCAPE_API void GetLayerContributionData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride);
LANDSCAPE_API void GetLayerContributionData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData);
LANDSCAPE_API void SetLayerContributionData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride);
template<typename TStoreData>
void GetDirtyDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData);
LANDSCAPE_API void GetDirtyData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride);
LANDSCAPE_API void GetDirtyData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData);
LANDSCAPE_API void SetDirtyData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride);
//
// XYOffsetmap access
//
template<typename T>
void SetXYOffsetDataTempl(int32 X1, int32 Y1, int32 X2, int32 Y2, const T* Data, int32 Stride);
LANDSCAPE_API void SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector2D* Data, int32 Stride);
LANDSCAPE_API void SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector* Data, int32 Stride);
// Helper accessor
LANDSCAPE_API FORCEINLINE FVector2D GetXYOffsetmapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData = NULL);
template<typename TStoreData>
void GetXYOffsetDataTempl(int32& X1, int32& Y1, int32& X2, int32& Y2, TStoreData& StoreData);
LANDSCAPE_API void GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector2D* Data, int32 Stride);
LANDSCAPE_API void GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, FVector2D>& SparseData);
LANDSCAPE_API void GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector* Data, int32 Stride);
LANDSCAPE_API void GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, FVector>& SparseData);
// Without data interpolation
template<typename TStoreData>
void GetXYOffsetDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData);
// Without data interpolation, able to get normal data
LANDSCAPE_API void GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector2D* Data, int32 Stride);
LANDSCAPE_API void GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, FVector2D>& SparseData);
LANDSCAPE_API void GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector* Data, int32 Stride);
LANDSCAPE_API void GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, FVector>& SparseData);
template<typename T>
static void ShrinkData(TArray<T>& Data, int32 OldMinX, int32 OldMinY, int32 OldMaxX, int32 OldMaxY, int32 NewMinX, int32 NewMinY, int32 NewMaxX, int32 NewMaxY);
LANDSCAPE_API const ALandscape* GetTargetLandscape() const;
LANDSCAPE_API bool CanHaveLandscapeLayersContent() const;
LANDSCAPE_API bool HasLandscapeLayersContent() const;
private:
int32 ComponentSizeQuads;
int32 SubsectionSizeQuads;
int32 ComponentNumSubsections;
FVector DrawScale;
ULandscapeInfo* LandscapeInfo;
// if true, we use the LandscapeActor->PrivateEditingLayer, otherwise we use the LocalEditLayerGUID specified below
bool bUseSharedLandscapeEditLayer;
FGuid LocalEditLayerGUID;
LANDSCAPE_API void FillLayer(ULandscapeLayerInfoObject* LayerInfo, bool bEmptyLayersOnly);
// Only for Missing Data interpolation... only internal usage
template<typename TData, typename TStoreData, typename FType>
FORCEINLINE void CalcMissingValues(const int32 X1, const int32 X2, const int32 Y1, const int32 Y2,
const int32 ComponentIndexX1, const int32 ComponentIndexX2, const int32 ComponentIndexY1, const int32 ComponentIndexY2,
const int32 ComponentSizeX, const int32 ComponentSizeY, TData* CornerValues,
TArray<bool>& NoBorderY1, TArray<bool>& NoBorderY2, TArray<bool>& ComponentDataExist, TStoreData& StoreData);
// Generic Height Data access
template<typename TStoreData, typename TGetHeightMapDataFunction>
void GetHeightDataInternal(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData, TGetHeightMapDataFunction GetHeightMapDataFunction);
LANDSCAPE_API FORCEINLINE FColor& GetHeightMapColor(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData);
// test if layer is allowed for a given texel
inline bool IsLayerAllowed(const ULandscapeLayerInfoObject* LayerInfo,
int32 ComponentIndexX, int32 SubIndexX, int32 SubX,
int32 ComponentIndexY, int32 SubIndexY, int32 SubY);
// counts the total influence of each weight-blended layer on this component
inline TMap<const ULandscapeLayerInfoObject*, uint32> CountWeightBlendedLayerInfluence(int32 ComponentIndexX, int32 ComponentIndexY, TOptional<TArrayView<const uint8* const>> LayerDataPtrs);
// chooses a replacement layer to use when erasing from 100% influence on a texel
LANDSCAPE_API const ULandscapeLayerInfoObject* ChooseReplacementLayer(const ULandscapeLayerInfoObject* LayerInfo, int32 ComponentIndexX, int32 SubIndexX, int32 SubX, int32 ComponentIndexY, int32 SubIndexY, int32 SubY, TMap<FIntPoint, TMap<const ULandscapeLayerInfoObject*, uint32>>& LayerInfluenceCache, TArrayView<const uint8* const> LayerDataPtrs);
};
struct FLandscapeDoNotDirtyScope
{
FLandscapeDoNotDirtyScope(FLandscapeEditDataInterface& InEditInterface, bool bInScopeEnabled = true)
: EditInterface(InEditInterface), bScopeEnabled(bInScopeEnabled)
{
if (bScopeEnabled)
{
bPreviousValue = EditInterface.GetShouldDirtyPackage();
EditInterface.SetShouldDirtyPackage(false);
}
}
~FLandscapeDoNotDirtyScope()
{
if (bScopeEnabled)
{
EditInterface.SetShouldDirtyPackage(bPreviousValue);
}
}
FLandscapeEditDataInterface& EditInterface;
bool bPreviousValue;
bool bScopeEnabled;
};
template<typename T>
void FLandscapeEditDataInterface::ShrinkData(TArray<T>& Data, int32 OldMinX, int32 OldMinY, int32 OldMaxX, int32 OldMaxY, int32 NewMinX, int32 NewMinY, int32 NewMaxX, int32 NewMaxY)
{
check(OldMinX <= OldMaxX && OldMinY <= OldMaxY);
check(NewMinX >= OldMinX && NewMaxX <= OldMaxX);
check(NewMinY >= OldMinY && NewMaxY <= OldMaxY);
if (NewMinX != OldMinX || NewMinY != OldMinY ||
NewMaxX != OldMaxX || NewMaxY != OldMaxY)
{
// if only the MaxY changes we don't need to do the moving, only the truncate
if (NewMinX != OldMinX || NewMinY != OldMinY || NewMaxX != OldMaxX)
{
for (int32 DestY = 0, SrcY = NewMinY - OldMinY; DestY <= NewMaxY - NewMinY; DestY++, SrcY++)
{
// UE_LOG(LogLandscape, Warning, TEXT("Dest: %d, %d = %d Src: %d, %d = %d Width = %d"), 0, DestY, DestY * (1 + NewMaxX - NewMinX), NewMinX - OldMinX, SrcY, SrcY * (1 + OldMaxX - OldMinX) + NewMinX - OldMinX, (1 + NewMaxX - NewMinX));
T* DestData = &Data[DestY * (1 + NewMaxX - NewMinX)];
const T* SrcData = &Data[SrcY * (1 + OldMaxX - OldMinX) + NewMinX - OldMinX];
FMemory::Memmove(DestData, SrcData, (1 + NewMaxX - NewMinX) * sizeof(T));
}
}
const int32 NewSize = (1 + NewMaxY - NewMinY) * (1 + NewMaxX - NewMinX);
Data.RemoveAt(NewSize, Data.Num() - NewSize);
}
}
//
// FHeightmapAccessor
//
template<bool bInUseInterp>
struct FHeightmapAccessor
{
enum { bUseInterp = bInUseInterp };
FHeightmapAccessor(ULandscapeInfo* InLandscapeInfo)
{
LandscapeInfo = InLandscapeInfo;
LandscapeEdit = new FLandscapeEditDataInterface(InLandscapeInfo);
}
void SetEditLayer(const FGuid& InEditLayerGUID)
{
LandscapeEdit->SetEditLayer(InEditLayerGUID);
}
FGuid GetEditLayer() const
{
return LandscapeEdit->GetEditLayer();
}
// accessors
void GetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint16>& Data)
{
LandscapeEdit->GetHeightData(X1, Y1, X2, Y2, Data);
}
void GetDataFast(int32 X1, int32 Y1, int32 X2, int32 Y2, TMap<FIntPoint, uint16>& Data)
{
LandscapeEdit->GetHeightDataFast(X1, Y1, X2, Y2, Data);
}
void GetDataFast(int32 X1, int32 Y1, int32 X2, int32 Y2, uint16* Data)
{
LandscapeEdit->GetHeightDataFast(X1, Y1, X2, Y2, Data, 0);
}
void SetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint16* Data, ELandscapeLayerPaintingRestriction PaintingRestriction = ELandscapeLayerPaintingRestriction::None)
{
TSet<ULandscapeComponent*> Components;
if (LandscapeInfo && LandscapeEdit->GetComponentsInRegion(X1, Y1, X2, Y2, &Components))
{
// Update data
ChangedComponents.Append(Components);
for (ULandscapeComponent* Component : Components)
{
Component->RequestHeightmapUpdate();
}
// Notify foliage to move any attached instances
bool bUpdateFoliage = false;
bool bUpdateNormals = false;
// Landscape Layers are updates are delayed and done in ALandscape::TickLayers
if (!LandscapeEdit->HasLandscapeLayersContent())
{
const bool bInvalidateLightingCache = true;
ALandscapeProxy::InvalidateGeneratedComponentData(Components, bInvalidateLightingCache);
bUpdateNormals = true;
for (ULandscapeComponent* Component : Components)
{
ULandscapeHeightfieldCollisionComponent* CollisionComponent = Component->GetCollisionComponent();
if (CollisionComponent && AInstancedFoliageActor::HasFoliageAttached(CollisionComponent))
{
bUpdateFoliage = true;
break;
}
}
}
if (bUpdateFoliage)
{
// Calculate landscape local-space bounding box of old data, to look for foliage instances.
TArray<ULandscapeHeightfieldCollisionComponent*> CollisionComponents;
CollisionComponents.Empty(Components.Num());
TArray<FBox> PreUpdateLocalBoxes;
PreUpdateLocalBoxes.Empty(Components.Num());
for (ULandscapeComponent* Component : Components)
{
ULandscapeHeightfieldCollisionComponent* CollisionComponent = Component->GetCollisionComponent();
if (CollisionComponent)
{
CollisionComponents.Add(CollisionComponent);
PreUpdateLocalBoxes.Add(FBox(FVector((float)X1, (float)Y1, Component->CachedLocalBox.Min.Z), FVector((float)X2, (float)Y2, Component->CachedLocalBox.Max.Z)));
}
}
// Update landscape.
LandscapeEdit->SetHeightData(X1, Y1, X2, Y2, Data, 0, bUpdateNormals);
// Snap foliage for each component.
for (int32 Index = 0; Index < CollisionComponents.Num(); ++Index)
{
ULandscapeHeightfieldCollisionComponent* CollisionComponent = CollisionComponents[Index];
CollisionComponent->SnapFoliageInstances(PreUpdateLocalBoxes[Index].TransformBy(LandscapeInfo->GetLandscapeProxy()->LandscapeActorToWorld().ToMatrixWithScale()).ExpandBy(1.0f));
}
}
else
{
// No foliage, just update landscape.
LandscapeEdit->SetHeightData(X1, Y1, X2, Y2, Data, 0, bUpdateNormals);
}
}
}
void Flush()
{
LandscapeEdit->Flush();
}
virtual ~FHeightmapAccessor()
{
// Flush here manually so it will release the lock of the textures, as we will re lock for other things afterward
Flush();
// Landscape Layers are updates are delayed and done in ALandscape::TickLayers
if (!LandscapeEdit->HasLandscapeLayersContent())
{
// Update the bounds and navmesh for the components we edited
for (TSet<ULandscapeComponent*>::TConstIterator It(ChangedComponents); It; ++It)
{
(*It)->UpdateCachedBounds();
(*It)->UpdateComponentToWorld();
// Recreate collision for modified components to update the physical materials
ULandscapeHeightfieldCollisionComponent* CollisionComponent = (*It)->GetCollisionComponent();
if (CollisionComponent)
{
CollisionComponent->RecreateCollision();
FNavigationSystem::UpdateComponentData(*CollisionComponent);
}
}
}
delete LandscapeEdit;
LandscapeEdit = NULL;
}
private:
ULandscapeInfo* LandscapeInfo;
FLandscapeEditDataInterface* LandscapeEdit;
TSet<ULandscapeComponent*> ChangedComponents;
};
//
// FAlphamapAccessor
//
template<bool bInUseInterp, bool bInUseTotalNormalize>
struct FAlphamapAccessor
{
enum { bUseInterp = bInUseInterp };
enum { bUseTotalNormalize = bInUseTotalNormalize };
FAlphamapAccessor(ULandscapeInfo* InLandscapeInfo, ULandscapeLayerInfoObject* InLayerInfo)
: LandscapeInfo(InLandscapeInfo)
, LandscapeEdit(InLandscapeInfo)
, LayerInfo(InLayerInfo)
, bBlendWeight(true)
{
// should be no Layer change during FAlphamapAccessor lifetime...
if (InLandscapeInfo && InLayerInfo)
{
if (LayerInfo == ALandscapeProxy::VisibilityLayer)
{
bBlendWeight = false;
}
else
{
bBlendWeight = !LayerInfo->bNoWeightBlend;
}
}
}
~FAlphamapAccessor()
{
if (!LandscapeEdit.HasLandscapeLayersContent())
{
// Recreate collision for modified components to update the physical materials
for (ULandscapeComponent* Component : ModifiedComponents)
{
ULandscapeHeightfieldCollisionComponent* CollisionComponent = Component->GetCollisionComponent();
if (CollisionComponent)
{
CollisionComponent->RecreateCollision();
// We need to trigger navigation mesh build, in case user have painted holes on a landscape
if (LayerInfo == ALandscapeProxy::VisibilityLayer)
{
FNavigationSystem::UpdateComponentData(*CollisionComponent);
}
}
}
}
}
void GetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint8>& Data)
{
LandscapeEdit.GetWeightData(LayerInfo, X1, Y1, X2, Y2, Data);
}
void GetDataFast(int32 X1, int32 Y1, int32 X2, int32 Y2, TMap<FIntPoint, uint8>& Data)
{
LandscapeEdit.GetWeightDataFast(LayerInfo, X1, Y1, X2, Y2, Data);
}
void GetDataFast(int32 X1, int32 Y1, int32 X2, int32 Y2, uint8* Data)
{
LandscapeEdit.GetWeightDataFast(LayerInfo, X1, Y1, X2, Y2, Data, 0);
}
void SetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, ELandscapeLayerPaintingRestriction PaintingRestriction)
{
TSet<ULandscapeComponent*> Components;
if (LandscapeEdit.GetComponentsInRegion(X1, Y1, X2, Y2, &Components))
{
for (ULandscapeComponent* LandscapeComponent : Components)
{
// Flag both modes depending on client calling SetData
LandscapeComponent->RequestWeightmapUpdate();
}
if (!LandscapeEdit.HasLandscapeLayersContent())
{
ALandscapeProxy::InvalidateGeneratedComponentData(Components);
ModifiedComponents.Append(Components);
}
LandscapeEdit.SetAlphaData(LayerInfo, X1, Y1, X2, Y2, Data, 0, PaintingRestriction, bBlendWeight, bUseTotalNormalize);
//LayerInfo->IsReferencedFromLoadedData = true;
}
}
void Flush()
{
LandscapeEdit.Flush();
}
void SetEditLayer(const FGuid& InEditLayerGUID)
{
LandscapeEdit.SetEditLayer(InEditLayerGUID);
}
FGuid GetEditLayer() const
{
return LandscapeEdit.GetEditLayer();
}
private:
ULandscapeInfo* LandscapeInfo;
FLandscapeEditDataInterface LandscapeEdit;
TSet<ULandscapeComponent*> ModifiedComponents;
ULandscapeLayerInfoObject* LayerInfo;
bool bBlendWeight;
};
#endif