5786 lines
238 KiB
C++
5786 lines
238 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
LandscapeEditInterface.cpp: Landscape editing interface
|
|
=============================================================================*/
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "GenericPlatform/GenericPlatformStackWalk.h"
|
|
#include "Templates/Greater.h"
|
|
#include "Containers/ArrayView.h"
|
|
#include "RenderingThread.h"
|
|
#include "LandscapeProxy.h"
|
|
#include "Landscape.h"
|
|
#include "LandscapeEditLayer.h"
|
|
#include "LandscapeInfo.h"
|
|
#include "LandscapeComponent.h"
|
|
#include "LandscapeLayerInfoObject.h"
|
|
|
|
#if WITH_EDITOR
|
|
|
|
#include "LandscapeDataAccess.h"
|
|
#include "LandscapeEdit.h"
|
|
#include "LandscapeRender.h"
|
|
#include "LandscapePrivate.h"
|
|
#include "ComponentReregisterContext.h"
|
|
#include "LandscapeTextureHash.h"
|
|
#include "Algo/Transform.h"
|
|
#include "TextureCompiler.h"
|
|
|
|
// Channel remapping
|
|
extern const size_t ChannelOffsets[4] = {STRUCT_OFFSET(FColor,R), STRUCT_OFFSET(FColor,G), STRUCT_OFFSET(FColor,B), STRUCT_OFFSET(FColor,A)};
|
|
|
|
//
|
|
// FLandscapeEditDataInterface
|
|
//
|
|
FLandscapeEditDataInterface::FLandscapeEditDataInterface(ULandscapeInfo* InLandscapeInfo, bool bInUploadTextureChangesToGPU)
|
|
: FLandscapeTextureDataInterface(bInUploadTextureChangesToGPU)
|
|
, bUseSharedLandscapeEditLayer(true)
|
|
, LocalEditLayerGUID()
|
|
{
|
|
if (InLandscapeInfo)
|
|
{
|
|
LandscapeInfo = InLandscapeInfo;
|
|
ComponentSizeQuads = InLandscapeInfo->ComponentSizeQuads;
|
|
SubsectionSizeQuads = InLandscapeInfo->SubsectionSizeQuads;
|
|
ComponentNumSubsections = InLandscapeInfo->ComponentNumSubsections;
|
|
DrawScale = InLandscapeInfo->DrawScale;
|
|
}
|
|
}
|
|
|
|
|
|
FLandscapeEditDataInterface::FLandscapeEditDataInterface(ULandscapeInfo* InLandscapeInfo, const FGuid& InEditLayerGUID, bool bInUploadTextureChangesToGPU)
|
|
: FLandscapeTextureDataInterface(bInUploadTextureChangesToGPU),
|
|
bUseSharedLandscapeEditLayer(false),
|
|
LocalEditLayerGUID(InEditLayerGUID)
|
|
|
|
{
|
|
if (InLandscapeInfo)
|
|
{
|
|
LandscapeInfo = InLandscapeInfo;
|
|
ComponentSizeQuads = InLandscapeInfo->ComponentSizeQuads;
|
|
SubsectionSizeQuads = InLandscapeInfo->SubsectionSizeQuads;
|
|
ComponentNumSubsections = InLandscapeInfo->ComponentNumSubsections;
|
|
DrawScale = InLandscapeInfo->DrawScale;
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetEditLayer(const FGuid& InEditLayerGUID)
|
|
{
|
|
bUseSharedLandscapeEditLayer = false;
|
|
LocalEditLayerGUID = InEditLayerGUID;
|
|
}
|
|
|
|
FGuid FLandscapeEditDataInterface::GetEditLayer() const
|
|
{
|
|
if (bUseSharedLandscapeEditLayer)
|
|
{
|
|
FGuid LandscapeEditLayerGuid;
|
|
if ((LandscapeInfo != nullptr) && LandscapeInfo->LandscapeActor.IsValid())
|
|
{
|
|
LandscapeEditLayerGuid = LandscapeInfo->LandscapeActor->GetEditingLayer();
|
|
}
|
|
return LandscapeEditLayerGuid;
|
|
}
|
|
return LocalEditLayerGUID;
|
|
}
|
|
|
|
FLandscapeTextureDataInterface::FLandscapeTextureDataInterface(bool bInUploadTextureChangesToGPU)
|
|
: bUploadTextureChangesToGPU(bInUploadTextureChangesToGPU), bShouldDirtyPackage(true)
|
|
{}
|
|
|
|
FLandscapeTextureDataInterface::~FLandscapeTextureDataInterface()
|
|
{
|
|
Flush();
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::Flush()
|
|
{
|
|
bool bNeedToWaitForUpdate = false;
|
|
|
|
if (bUploadTextureChangesToGPU)
|
|
{
|
|
// Update all textures
|
|
for (TMap<UTexture2D*, FLandscapeTextureDataInfo*>::TIterator It(TextureDataMap); It; ++It)
|
|
{
|
|
if (It.Value()->UpdateTextureData())
|
|
{
|
|
bNeedToWaitForUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bNeedToWaitForUpdate )
|
|
{
|
|
FlushRenderingCommands();
|
|
}
|
|
|
|
// delete all the FLandscapeTextureDataInfo allocations
|
|
for( TMap<UTexture2D*, FLandscapeTextureDataInfo*>::TIterator It(TextureDataMap); It; ++It )
|
|
{
|
|
delete It.Value(); // FLandscapeTextureDataInfo destructors will unlock any texture data
|
|
}
|
|
|
|
TextureDataMap.Empty();
|
|
}
|
|
|
|
#include "LevelUtils.h"
|
|
|
|
void ALandscape::CalcComponentIndicesOverlap(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const int32 ComponentSizeQuads,
|
|
int32& ComponentIndexX1, int32& ComponentIndexY1, int32& ComponentIndexX2, int32& ComponentIndexY2)
|
|
{
|
|
// Find component range for this block of data
|
|
ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components
|
|
ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1;
|
|
ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1;
|
|
ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1;
|
|
}
|
|
|
|
void ALandscape::CalcComponentIndicesNoOverlap(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const int32 ComponentSizeQuads,
|
|
int32& ComponentIndexX1, int32& ComponentIndexY1, int32& ComponentIndexX2, int32& ComponentIndexY2)
|
|
{
|
|
// Find component range for this block of data
|
|
ComponentIndexX1 = (X1 >= 0) ? X1 / ComponentSizeQuads : (X1+1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components
|
|
ComponentIndexY1 = (Y1 >= 0) ? Y1 / ComponentSizeQuads : (Y1+1) / ComponentSizeQuads - 1;
|
|
ComponentIndexX2 = (X2-1 >= 0) ? (X2-1) / ComponentSizeQuads : (X2) / ComponentSizeQuads - 1;
|
|
ComponentIndexY2 = (Y2-1 >= 0) ? (Y2-1) / ComponentSizeQuads : (Y2) / ComponentSizeQuads - 1;
|
|
// Shrink indices for shared values
|
|
if ( ComponentIndexX2 < ComponentIndexX1)
|
|
{
|
|
ComponentIndexX2 = ComponentIndexX1;
|
|
}
|
|
if ( ComponentIndexY2 < ComponentIndexY1)
|
|
{
|
|
ComponentIndexY2 = ComponentIndexY1;
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
// Ugly helper function, all arrays should be only size 4
|
|
template<typename T, typename F>
|
|
FORCEINLINE void CalcInterpValue(const int32* Dist, const bool* Exist, const T* Value, F& ValueX, F& ValueY)
|
|
{
|
|
if (Exist[0] && Exist[1])
|
|
{
|
|
ValueX = (F)(Dist[1] * Value[0] + Dist[0] * Value[1]) / (Dist[0] + Dist[1]);
|
|
}
|
|
else
|
|
{
|
|
if (Exist[0])
|
|
{
|
|
ValueX = Value[0];
|
|
}
|
|
else if (Exist[1])
|
|
{
|
|
ValueX = Value[1];
|
|
}
|
|
}
|
|
|
|
if (Exist[2] && Exist[3])
|
|
{
|
|
ValueY = (F)(Dist[3] * Value[2] + Dist[2] * Value[3]) / (Dist[2] + Dist[3]);
|
|
}
|
|
else
|
|
{
|
|
if (Exist[2])
|
|
{
|
|
ValueY = Value[2];
|
|
}
|
|
else if (Exist[3])
|
|
{
|
|
ValueY = Value[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T, typename F>
|
|
FORCEINLINE T CalcValueFromValueXY( const int32* Dist, const F& ValueX, const F& ValueY, const uint8& CornerSet, const T* CornerValues )
|
|
{
|
|
T FinalValue;
|
|
int32 DistX = FMath::Min(Dist[0], Dist[1]);
|
|
int32 DistY = FMath::Min(Dist[2], Dist[3]);
|
|
if (DistX+DistY > 0)
|
|
{
|
|
FinalValue = static_cast<T>(((ValueX * DistY) + (ValueY * DistX)) / static_cast<F>((DistX + DistY)));
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && Dist[0] == 0 && Dist[2] == 0)
|
|
{
|
|
FinalValue = CornerValues[0];
|
|
}
|
|
else if ((CornerSet & 1 << 1) && Dist[1] == 0 && Dist[2] == 0)
|
|
{
|
|
FinalValue = CornerValues[1];
|
|
}
|
|
else if ((CornerSet & 1 << 2) && Dist[0] == 0 && Dist[3] == 0)
|
|
{
|
|
FinalValue = CornerValues[2];
|
|
}
|
|
else if ((CornerSet & 1 << 3) && Dist[1] == 0 && Dist[3] == 0)
|
|
{
|
|
FinalValue = CornerValues[3];
|
|
}
|
|
else
|
|
{
|
|
FinalValue = static_cast<T>(ValueX);
|
|
}
|
|
}
|
|
return FinalValue;
|
|
}
|
|
|
|
};
|
|
|
|
bool FLandscapeEditDataInterface::GetComponentsInRegion(int32 X1, int32 Y1, int32 X2, int32 Y2, TSet<ULandscapeComponent*>* OutComponents /*= nullptr*/)
|
|
{
|
|
if (ComponentSizeQuads <= 0 || !LandscapeInfo)
|
|
{
|
|
return false;
|
|
}
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
bool bNotLocked = true;
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
|
|
if (Component)
|
|
{
|
|
bNotLocked = bNotLocked && (!FLevelUtils::IsLevelLocked(Component->GetLandscapeProxy()->GetLevel())) && FLevelUtils::IsLevelVisible(Component->GetLandscapeProxy()->GetLevel());
|
|
if (OutComponents)
|
|
{
|
|
OutComponents->Add(Component);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bNotLocked;
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetHeightData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint16* InData, int32 InStride, bool InCalcNormals, const uint16* InNormalData, const uint16* InHeightAlphaBlendData, const uint8* InHeightFlagsData, bool InCreateComponents, UTexture2D* InHeightmap, UTexture2D* InXYOffsetmapTexture,
|
|
bool InUpdateBounds, bool InUpdateCollision, bool InGenerateMips)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FLandscapeEditDataInterface::SetHeightData);
|
|
|
|
const int32 NumVertsX = 1 + X2 - X1;
|
|
const int32 NumVertsY = 1 + Y2 - Y1;
|
|
|
|
if (InStride == 0)
|
|
{
|
|
InStride = NumVertsX;
|
|
}
|
|
|
|
check(ComponentSizeQuads > 0);
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
FVector* VertexNormals = nullptr;
|
|
if (InCalcNormals)
|
|
{
|
|
// Calculate the normals for each of the two triangles per quad.
|
|
// Note that the normals at the edges are not correct because they include normals
|
|
// from triangles outside the current area. They are not updated
|
|
VertexNormals = new FVector[NumVertsX*NumVertsY];
|
|
FMemory::Memzero(VertexNormals, NumVertsX*NumVertsY * sizeof(FVector));
|
|
|
|
// Need to consider XYOffset for XY displacemented map
|
|
FVector2D* XYOffsets = new FVector2D[NumVertsX*NumVertsY];
|
|
FMemory::Memzero(XYOffsets, NumVertsX*NumVertsY * sizeof(FVector2D));
|
|
GetXYOffsetDataFast(X1, Y1, X2, Y2, XYOffsets, 0);
|
|
|
|
for (int32 Y = 0; Y < NumVertsY - 1; Y++)
|
|
{
|
|
for (int32 X = 0; X < NumVertsX - 1; X++)
|
|
{
|
|
FVector Vert00 = FVector(XYOffsets[(X + 0) + NumVertsX*(Y + 0)].X + 0.0f, XYOffsets[(X + 0) + NumVertsX*(Y + 0)].Y + 0.0f, LandscapeDataAccess::GetLocalHeight(InData[(X + 0) + InStride*(Y + 0)])) * DrawScale;
|
|
FVector Vert01 = FVector(XYOffsets[(X + 0) + NumVertsX*(Y + 0)].X + 0.0f, XYOffsets[(X + 0) + NumVertsX*(Y + 0)].Y + 1.0f, LandscapeDataAccess::GetLocalHeight(InData[(X + 0) + InStride*(Y + 1)])) * DrawScale;
|
|
FVector Vert10 = FVector(XYOffsets[(X + 0) + NumVertsX*(Y + 0)].X + 1.0f, XYOffsets[(X + 0) + NumVertsX*(Y + 0)].Y + 0.0f, LandscapeDataAccess::GetLocalHeight(InData[(X + 1) + InStride*(Y + 0)])) * DrawScale;
|
|
FVector Vert11 = FVector(XYOffsets[(X + 0) + NumVertsX*(Y + 0)].X + 1.0f, XYOffsets[(X + 0) + NumVertsX*(Y + 0)].Y + 1.0f, LandscapeDataAccess::GetLocalHeight(InData[(X + 1) + InStride*(Y + 1)])) * DrawScale;
|
|
|
|
FVector FaceNormal1 = ((Vert00 - Vert10) ^ (Vert10 - Vert11)).GetSafeNormal();
|
|
FVector FaceNormal2 = ((Vert11 - Vert01) ^ (Vert01 - Vert00)).GetSafeNormal();
|
|
|
|
// contribute to the vertex normals.
|
|
VertexNormals[(X + 1 + NumVertsX*(Y + 0))] += FaceNormal1;
|
|
VertexNormals[(X + 0 + NumVertsX*(Y + 1))] += FaceNormal2;
|
|
VertexNormals[(X + 0 + NumVertsX*(Y + 0))] += FaceNormal1 + FaceNormal2;
|
|
VertexNormals[(X + 1 + NumVertsX*(Y + 1))] += FaceNormal1 + FaceNormal2;
|
|
}
|
|
}
|
|
|
|
delete[] XYOffsets;
|
|
}
|
|
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
FIntPoint ComponentKey(ComponentIndexX, ComponentIndexY);
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey);
|
|
|
|
// if nullptr, it was painted away
|
|
if (Component == nullptr)
|
|
{
|
|
if (InCreateComponents)
|
|
{
|
|
// not yet implemented
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
UTexture2D* Heightmap = InHeightmap != nullptr ? InHeightmap : Component->GetHeightmap(GetEditLayer());
|
|
UTexture2D* XYOffsetmapTexture = InXYOffsetmapTexture != nullptr ? InXYOffsetmapTexture : ToRawPtr(Component->XYOffsetmapTexture);
|
|
|
|
Component->Modify(GetShouldDirtyPackage());
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Heightmap);
|
|
FColor* HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
|
|
FColor* XYOffsetMipData = nullptr;
|
|
if (XYOffsetmapTexture)
|
|
{
|
|
FLandscapeTextureDataInfo* XYTexDataInfo = GetTextureDataInfo(XYOffsetmapTexture);
|
|
XYOffsetMipData = (FColor*)XYTexDataInfo->GetMipData(Component->CollisionMipLevel);
|
|
}
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = Heightmap->Source.GetSizeX();
|
|
int32 SizeV = Heightmap->Source.GetSizeY();
|
|
int32 HeightmapOffsetX = static_cast<int32>(Component->HeightmapScaleBias.Z * SizeU);
|
|
int32 HeightmapOffsetY = static_cast<int32>(Component->HeightmapScaleBias.W * SizeV);
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
|
|
// To adjust bounding box
|
|
uint16 MinHeight = MAX_uint16;
|
|
uint16 MaxHeight = 0;
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
checkSlow(LandscapeX >= X1 && LandscapeX <= X2);
|
|
checkSlow(LandscapeY >= Y1 && LandscapeY <= Y2);
|
|
|
|
// Find the input data corresponding to this vertex
|
|
int32 DataIndex = (LandscapeX - X1) + InStride * (LandscapeY - Y1);
|
|
const uint16& Height = InData[DataIndex];
|
|
|
|
// for bounding box
|
|
if (Height < MinHeight)
|
|
{
|
|
MinHeight = Height;
|
|
}
|
|
if (Height > MaxHeight)
|
|
{
|
|
MaxHeight = Height;
|
|
}
|
|
|
|
int32 TexX = HeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX;
|
|
int32 TexY = HeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY;
|
|
FColor& TexData = HeightmapTextureData[TexX + TexY * SizeU];
|
|
|
|
// Update the texture
|
|
TexData.R = Height >> 8;
|
|
TexData.G = Height & 255;
|
|
|
|
// Update normals if we're not on an edge vertex
|
|
if (VertexNormals && LandscapeX > X1 && LandscapeX < X2 && LandscapeY > Y1 && LandscapeY < Y2)
|
|
{
|
|
const int32 NormalDataIndex = (LandscapeX - X1) + NumVertsX * (LandscapeY - Y1);
|
|
FVector Normal = VertexNormals[NormalDataIndex].GetSafeNormal();
|
|
TexData.B = static_cast<uint8>(FMath::RoundToInt32(127.5f * (Normal.X + 1.0f)));
|
|
TexData.A = static_cast<uint8>(FMath::RoundToInt32(127.5f * (Normal.Y + 1.0f)));
|
|
}
|
|
else if (InNormalData)
|
|
{
|
|
// Need data validation?
|
|
const uint16& Normal = InNormalData[DataIndex];
|
|
TexData.B = Normal >> 8;
|
|
TexData.A = Normal & 255;
|
|
}
|
|
else if (InHeightAlphaBlendData)
|
|
{
|
|
const uint16& HeightAlphaBlend = InHeightAlphaBlendData[DataIndex];
|
|
TexData.B = HeightAlphaBlend >> 8;
|
|
TexData.A = HeightAlphaBlend & 0xFC;
|
|
TexData.A |= (InHeightFlagsData ? InHeightFlagsData[DataIndex] : 3) & 0x3;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
int32 TexX1 = HeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX1;
|
|
int32 TexY1 = HeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY1;
|
|
int32 TexX2 = HeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX2;
|
|
int32 TexY2 = HeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY2;
|
|
TexDataInfo->AddMipUpdateRegion(0, TexX1, TexY1, TexX2, TexY2);
|
|
}
|
|
}
|
|
|
|
Component->RequestHeightmapUpdate();
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
// See if we need to adjust the bounds. Note we never shrink the bounding box at this point
|
|
bool bUpdateBoxSphereBounds = false;
|
|
|
|
if (InUpdateBounds)
|
|
{
|
|
float MinLocalZ = LandscapeDataAccess::GetLocalHeight(MinHeight);
|
|
float MaxLocalZ = LandscapeDataAccess::GetLocalHeight(MaxHeight);
|
|
|
|
if (MinLocalZ < Component->CachedLocalBox.Min.Z)
|
|
{
|
|
Component->CachedLocalBox.Min.Z = MinLocalZ;
|
|
bUpdateBoxSphereBounds = true;
|
|
}
|
|
if (MaxLocalZ > Component->CachedLocalBox.Max.Z)
|
|
{
|
|
Component->CachedLocalBox.Max.Z = MaxLocalZ;
|
|
bUpdateBoxSphereBounds = true;
|
|
}
|
|
|
|
if (bUpdateBoxSphereBounds)
|
|
{
|
|
Component->UpdateComponentToWorld();
|
|
}
|
|
}
|
|
|
|
// Update mipmaps
|
|
|
|
// Work out how many mips should be calculated directly from one component's data.
|
|
// The remaining mips are calculated on a per texture basis.
|
|
// eg if subsection is 7x7 quads, we need one 3 mips total: (8x8, 4x4, 2x2 verts)
|
|
if (InGenerateMips)
|
|
{
|
|
int32 BaseNumMips = FMath::CeilLogTwo(SubsectionSizeQuads + 1);
|
|
TArray<FColor*> MipData;
|
|
MipData.AddUninitialized(BaseNumMips);
|
|
MipData[0] = HeightmapTextureData;
|
|
for (int32 MipIdx = 1; MipIdx < BaseNumMips; MipIdx++)
|
|
{
|
|
MipData[MipIdx] = (FColor*)TexDataInfo->GetMipData(MipIdx);
|
|
}
|
|
Component->GenerateHeightmapMips(MipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfo);
|
|
|
|
if (InUpdateCollision)
|
|
{
|
|
// Update collision
|
|
Component->UpdateCollisionHeightData(
|
|
MipData[Component->CollisionMipLevel],
|
|
Component->SimpleCollisionMipLevel > Component->CollisionMipLevel ? MipData[Component->SimpleCollisionMipLevel] : nullptr,
|
|
ComponentX1, ComponentY1, ComponentX2, ComponentY2, bUpdateBoxSphereBounds,
|
|
XYOffsetMipData);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// In Layer, cumulate dirty collision region (will be used next time UpdateCollisionHeightData is called)
|
|
Component->UpdateDirtyCollisionHeightData(FIntRect(ComponentX1, ComponentY1, ComponentX2, ComponentY2));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VertexNormals)
|
|
{
|
|
delete[] VertexNormals;
|
|
}
|
|
}
|
|
|
|
//
|
|
// RecalculateNormals - Regenerate normals for the entire landscape. Called after modifying DrawScale3D.
|
|
//
|
|
void FLandscapeEditDataInterface::RecalculateNormals()
|
|
{
|
|
if (!LandscapeInfo) return;
|
|
|
|
// we should only ever be calculating normals on the final runtime layer.
|
|
// if this gets hit, then we are wasting time calculating them on edit layers.
|
|
ensure(!GetEditLayer().IsValid());
|
|
|
|
// Recalculate normals for each component in turn
|
|
for( auto It = LandscapeInfo->XYtoComponentMap.CreateIterator(); It; ++It )
|
|
{
|
|
ULandscapeComponent* Component = It.Value();
|
|
|
|
// one extra row of vertex either side of the component
|
|
int32 X1 = Component->GetSectionBase().X-1;
|
|
int32 Y1 = Component->GetSectionBase().Y-1;
|
|
int32 X2 = Component->GetSectionBase().X+ComponentSizeQuads+1;
|
|
int32 Y2 = Component->GetSectionBase().Y+ComponentSizeQuads+1;
|
|
const int32 OriginalX1 = X1;
|
|
const int32 OriginalY1 = Y1;
|
|
const int32 OriginalX2 = X2;
|
|
const int32 OriginalY2 = Y2;
|
|
int32 Stride = ComponentSizeQuads+3;
|
|
int32 StrideSquared = FMath::Square(Stride);
|
|
|
|
uint16* HeightData = new uint16[StrideSquared];
|
|
FVector* VertexNormals = new FVector[StrideSquared];
|
|
FMemory::Memzero(VertexNormals, StrideSquared * sizeof(FVector));
|
|
FVector2D* XYOffsets = new FVector2D[StrideSquared];
|
|
FMemory::Memzero(XYOffsets, StrideSquared * sizeof(FVector2D));
|
|
|
|
// Get XY offset
|
|
GetXYOffsetDataFast(X1,Y1,X2,Y2,XYOffsets,0);
|
|
check(X1 == OriginalX1 && Y1 == OriginalY1 && X2 == OriginalX2 && Y2 == OriginalY2); // because we're querying within an existing component, we assume the data should always exist
|
|
|
|
// Get the vertex positions for entire quad
|
|
GetHeightData(X1,Y1,X2,Y2,HeightData,0);
|
|
check(X1 == OriginalX1 && Y1 == OriginalY1 && X2 == OriginalX2 && Y2 == OriginalY2); // because we're querying within an existing component, we assume the data should always exist
|
|
|
|
// Contribute face normals for all triangles contributing to this components' normals
|
|
for( int32 Y=0;Y<Stride-1;Y++ )
|
|
{
|
|
for( int32 X=0;X<Stride-1;X++ )
|
|
{
|
|
FVector Vert00 = FVector(XYOffsets[(X + 0) + Stride * (Y + 0)].X + 0.0f, XYOffsets[(X + 0) + Stride * (Y + 0)].Y + 0.0f, LandscapeDataAccess::GetLocalHeight(HeightData[(X + 0) + Stride * (Y + 0)])) * DrawScale;
|
|
FVector Vert01 = FVector(XYOffsets[(X + 0) + Stride * (Y + 0)].X + 0.0f, XYOffsets[(X + 0) + Stride * (Y + 0)].Y + 1.0f, LandscapeDataAccess::GetLocalHeight(HeightData[(X + 0) + Stride * (Y + 1)])) * DrawScale;
|
|
FVector Vert10 = FVector(XYOffsets[(X + 0) + Stride * (Y + 0)].X + 1.0f, XYOffsets[(X + 0) + Stride * (Y + 0)].Y + 0.0f, LandscapeDataAccess::GetLocalHeight(HeightData[(X + 1) + Stride * (Y + 0)])) * DrawScale;
|
|
FVector Vert11 = FVector(XYOffsets[(X + 0) + Stride * (Y + 0)].X + 1.0f, XYOffsets[(X + 0) + Stride * (Y + 0)].Y + 1.0f, LandscapeDataAccess::GetLocalHeight(HeightData[(X + 1) + Stride * (Y + 1)])) * DrawScale;
|
|
|
|
FVector FaceNormal1 = ((Vert00-Vert10) ^ (Vert10-Vert11)).GetSafeNormal();
|
|
FVector FaceNormal2 = ((Vert11-Vert01) ^ (Vert01-Vert00)).GetSafeNormal();
|
|
|
|
// contribute to the vertex normals.
|
|
VertexNormals[(X+1 + Stride*(Y+0))] += FaceNormal1;
|
|
VertexNormals[(X+0 + Stride*(Y+1))] += FaceNormal2;
|
|
VertexNormals[(X+0 + Stride*(Y+0))] += FaceNormal1 + FaceNormal2;
|
|
VertexNormals[(X+1 + Stride*(Y+1))] += FaceNormal1 + FaceNormal2;
|
|
}
|
|
}
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
UTexture2D* EditHeightmap = Component->GetHeightmap(GetEditLayer());
|
|
int32 SizeU = EditHeightmap->Source.GetSizeX();
|
|
int32 SizeV = EditHeightmap->Source.GetSizeY();
|
|
int32 HeightmapOffsetX = static_cast<int32>(Component->HeightmapScaleBias.Z * SizeU);
|
|
int32 HeightmapOffsetY = static_cast<int32>(Component->HeightmapScaleBias.W * SizeV);
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(EditHeightmap);
|
|
FColor* HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
|
|
// Apply vertex normals to the component
|
|
for( int32 SubIndexY=0;SubIndexY<Component->NumSubsections;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=0;SubIndexX<Component->NumSubsections;SubIndexX++ )
|
|
{
|
|
for( int32 SubY=0;SubY<=SubsectionSizeQuads;SubY++ )
|
|
{
|
|
for( int32 SubX=0;SubX<=SubsectionSizeQuads;SubX++ )
|
|
{
|
|
// Read from local VertexNormals cache which doesn't have duplicated values.
|
|
const int32 ReadX = (SubsectionSizeQuads) * SubIndexX + SubX;
|
|
const int32 ReadY = (SubsectionSizeQuads) * SubIndexY + SubY;
|
|
const int32 ReadIndex = (ReadX + 1) + (ReadY + 1) * Stride;
|
|
checkSlow(ReadIndex < StrideSquared);
|
|
const FVector Normal = VertexNormals[ReadIndex].GetSafeNormal();
|
|
|
|
// Write to (shared) Heightmap texture which has duplicated values on subsection and section borders.
|
|
const int32 WriteX = HeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX;
|
|
const int32 WriteY = HeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY;
|
|
const int32 WriteIndex = WriteX + WriteY * SizeU;
|
|
FColor& TexData = HeightmapTextureData[WriteIndex];
|
|
|
|
// Update the channels containing the normals
|
|
TexData.B = static_cast<uint8>(FMath::RoundToInt32( 127.5f * (Normal.X + 1.0f) ));
|
|
TexData.A = static_cast<uint8>(FMath::RoundToInt32( 127.5f * (Normal.Y + 1.0f) ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete[] XYOffsets;
|
|
delete[] HeightData;
|
|
delete[] VertexNormals;
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
int32 TexX1 = HeightmapOffsetX;
|
|
int32 TexY1 = HeightmapOffsetY;
|
|
int32 TexX2 = HeightmapOffsetX + (SubsectionSizeQuads+1) * Component->NumSubsections - 1;
|
|
int32 TexY2 = HeightmapOffsetY + (SubsectionSizeQuads+1) * Component->NumSubsections - 1;
|
|
TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2);
|
|
|
|
// Work out how many mips should be calculated directly from one component's data.
|
|
// The remaining mips are calculated on a per texture basis.
|
|
// eg if subsection is 7x7 quads, we need one 3 mips total: (8x8, 4x4, 2x2 verts)
|
|
int32 BaseNumMips = FMath::CeilLogTwo(SubsectionSizeQuads+1);
|
|
TArray<FColor*> MipData;
|
|
MipData.AddUninitialized(BaseNumMips);
|
|
MipData[0] = HeightmapTextureData;
|
|
for( int32 MipIdx=1;MipIdx<BaseNumMips;MipIdx++ )
|
|
{
|
|
MipData[MipIdx] = (FColor*)TexDataInfo->GetMipData(MipIdx);
|
|
}
|
|
Component->GenerateHeightmapMips( MipData, 0, 0, ComponentSizeQuads, ComponentSizeQuads, TexDataInfo );
|
|
}
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetHeightDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData, UTexture2D* InHeightmap, TStoreData* NormalData /*= NULL*/)
|
|
{
|
|
if (!LandscapeInfo) return;
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
|
|
|
|
if (Component == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UTexture2D* Heightmap = InHeightmap != nullptr ? InHeightmap : Component->GetHeightmap(GetEditLayer());
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
FColor* HeightmapTextureData = NULL;
|
|
TexDataInfo = GetTextureDataInfo(Heightmap);
|
|
HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = Heightmap->Source.GetSizeX();
|
|
int32 SizeV = Heightmap->Source.GetSizeY();
|
|
int32 HeightmapOffsetX = static_cast<int32>(Component->HeightmapScaleBias.Z * SizeU);
|
|
int32 HeightmapOffsetY = static_cast<int32>(Component->HeightmapScaleBias.W * SizeV);
|
|
|
|
int32 TexX = HeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = HeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
FColor& TexData = HeightmapTextureData[ TexX + TexY * SizeU ];
|
|
|
|
uint16 Height = (((uint16)TexData.R) << 8) | TexData.G;
|
|
StoreData.Store(LandscapeX, LandscapeY, Height);
|
|
if (NormalData)
|
|
{
|
|
uint16 Normals = (((uint16)TexData.B) << 8) | TexData.A;
|
|
NormalData->Store(LandscapeX, LandscapeY, Normals);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename TData, typename TStoreData, typename FType>
|
|
void FLandscapeEditDataInterface::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)
|
|
{
|
|
bool NoBorderX1 = false, NoBorderX2 = false;
|
|
// Init data...
|
|
FMemory::Memzero(NoBorderY1.GetData(), ComponentSizeX*sizeof(bool));
|
|
FMemory::Memzero(NoBorderY2.GetData(), ComponentSizeX*sizeof(bool));
|
|
int32 BorderX1 = INT_MAX, BorderX2 = INT_MIN;
|
|
TArray<int32> BorderY1, BorderY2;
|
|
BorderY1.Empty(ComponentSizeX);
|
|
BorderY2.Empty(ComponentSizeX);
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
new (BorderY1) int32(INT_MAX);
|
|
new (BorderY2) int32(INT_MIN);
|
|
}
|
|
|
|
// fill up missing values...
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
NoBorderX1 = false;
|
|
NoBorderX2 = false;
|
|
BorderX1 = INT_MAX;
|
|
BorderX2 = INT_MIN;
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1;
|
|
if (!ComponentDataExist[ComponentIndexXY])
|
|
{
|
|
int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1;
|
|
int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1;
|
|
|
|
uint8 CornerSet = 0;
|
|
bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ];
|
|
bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ];
|
|
|
|
// Search for neighbor component for interpolation
|
|
bool bShouldSearchX = (BorderX2 <= ComponentIndexX);
|
|
bool bShouldSearchY = (BorderY2[ComponentIndexXX] <= ComponentIndexY);
|
|
// Search for left-closest component
|
|
if ( bShouldSearchX || (!NoBorderX1 && BorderX1 == INT_MAX) )
|
|
{
|
|
NoBorderX1 = true;
|
|
BorderX1 = INT_MAX;
|
|
for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--)
|
|
{
|
|
if (ComponentDataExist[ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + X-ComponentIndexX1])
|
|
{
|
|
NoBorderX1 = false;
|
|
BorderX1 = X;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for right-closest component
|
|
if ( bShouldSearchX || (!NoBorderX2 && BorderX2 == INT_MIN) )
|
|
{
|
|
NoBorderX2 = true;
|
|
BorderX2 = INT_MIN;
|
|
for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++)
|
|
{
|
|
if (ComponentDataExist[ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + X-ComponentIndexX1])
|
|
{
|
|
NoBorderX2 = false;
|
|
BorderX2 = X;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for up-closest component
|
|
if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && BorderY1[ComponentIndexXX] == INT_MAX))
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = true;
|
|
BorderY1[ComponentIndexXX] = INT_MAX;
|
|
for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--)
|
|
{
|
|
if (ComponentDataExist[ComponentSizeX*(Y-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1])
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = false;
|
|
BorderY1[ComponentIndexXX] = Y;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for bottom-closest component
|
|
if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && BorderY2[ComponentIndexXX] == INT_MIN))
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = true;
|
|
BorderY2[ComponentIndexXX] = INT_MIN;
|
|
for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++)
|
|
{
|
|
if (ComponentDataExist[ComponentSizeX*(Y-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1])
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = false;
|
|
BorderY2[ComponentIndexXX] = Y;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((ComponentIndexX == ComponentIndexX1) || (ComponentIndexY == ComponentIndexY1)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY-1-ComponentIndexY1) + ComponentIndexX-1-ComponentIndexX1])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads));
|
|
}
|
|
if (((ComponentIndexX == ComponentIndexX2) || (ComponentIndexY == ComponentIndexY1)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY-1-ComponentIndexY1) + ComponentIndexX+1-ComponentIndexX1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = TData(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads));
|
|
}
|
|
if (((ComponentIndexX == ComponentIndexX1) || (ComponentIndexY == ComponentIndexY2)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY+1-ComponentIndexY1) + ComponentIndexX-1-ComponentIndexX1])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads));
|
|
}
|
|
if (((ComponentIndexX == ComponentIndexX2) || (ComponentIndexY == ComponentIndexY2)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY+1-ComponentIndexY1) + ComponentIndexX+1-ComponentIndexX1])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = TData(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads));
|
|
}
|
|
|
|
FillCornerValues(CornerSet, CornerValues);
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
TData Value[4];
|
|
FMemory::Memzero(Value, sizeof(TData)* 4);
|
|
int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX};
|
|
FType ValueX, ValueY;
|
|
FMemory::Memzero(&ValueX, sizeof(FType));
|
|
FMemory::Memzero(&ValueY, sizeof(FType));
|
|
bool Exist[4] = {false, false, false, false};
|
|
|
|
if (ExistLeft)
|
|
{
|
|
Value[0] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, LandscapeY));
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else if ( BorderX1 != INT_MAX )
|
|
{
|
|
int32 BorderIdxX = (BorderX1+1)*ComponentSizeQuads;
|
|
Value[0] = TData(StoreData.Load(BorderIdxX, LandscapeY));
|
|
Dist[0] = LandscapeX - (BorderIdxX-1);
|
|
Exist[0] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 2))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[0] = static_cast<TData>((FType)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2));
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
}
|
|
|
|
if ( BorderX2 != INT_MIN )
|
|
{
|
|
int32 BorderIdxX = BorderX2*ComponentSizeQuads;
|
|
Value[1] = TData(StoreData.Load(BorderIdxX, LandscapeY));
|
|
Dist[1] = (BorderIdxX+1) - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[1] = static_cast<TData>((FType)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
}
|
|
|
|
if (ExistUp)
|
|
{
|
|
Value[2] = TData(StoreData.Load(LandscapeX, ComponentIndexY*ComponentSizeQuads));
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else if ( BorderY1[ComponentIndexXX] != INT_MAX )
|
|
{
|
|
int32 BorderIdxY = (BorderY1[ComponentIndexXX]+1)*ComponentSizeQuads;
|
|
Value[2] = TData(StoreData.Load(LandscapeX, BorderIdxY));
|
|
Dist[2] = LandscapeY - BorderIdxY;
|
|
Exist[2] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 1))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[2] = static_cast<TData>((FType)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2));
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
}
|
|
|
|
if ( BorderY2[ComponentIndexXX] != INT_MIN )
|
|
{
|
|
int32 BorderIdxY = BorderY2[ComponentIndexXX]*ComponentSizeQuads;
|
|
Value[3] = TData(StoreData.Load(LandscapeX, BorderIdxY));
|
|
Dist[3] = BorderIdxY - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[3] = static_cast<TData>((FType)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
}
|
|
|
|
CalcInterpValue<TData, FType>(Dist, Exist, Value, ValueX, ValueY);
|
|
|
|
TData FinalValue; // Default Value
|
|
FMemory::Memzero(&FinalValue, sizeof(TData));
|
|
if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = CalcValueFromValueXY<TData>(Dist, ValueX, ValueY, CornerSet, CornerValues);
|
|
}
|
|
else if ( (Exist[0] || Exist[1]) )
|
|
{
|
|
FinalValue = static_cast<TData>(ValueX);
|
|
}
|
|
else if ( (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = static_cast<TData>(ValueY);
|
|
}
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, FinalValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FColor& FLandscapeEditDataInterface::GetHeightMapColor(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData)
|
|
{
|
|
UTexture2D* Heightmap = Component->GetHeightmap(GetEditLayer());
|
|
check(Component);
|
|
if (!TextureData)
|
|
{
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Heightmap);
|
|
TextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
}
|
|
|
|
// All Heightmaps of component have the same texture size
|
|
const FTextureSource& HeightmapTextureSource = Heightmap->Source;
|
|
const int32 SizeU = HeightmapTextureSource.GetSizeX();
|
|
const int32 SizeV = HeightmapTextureSource.GetSizeY();
|
|
const int32 HeightmapOffsetX = static_cast<int32>(Component->HeightmapScaleBias.Z * SizeU);
|
|
const int32 HeightmapOffsetY = static_cast<int32>(Component->HeightmapScaleBias.W * SizeV);
|
|
|
|
|
|
const int32 TexX = HeightmapOffsetX + TexU;
|
|
const int32 TexY = HeightmapOffsetY + TexV;
|
|
FColor& TexData = TextureData[ TexX + TexY * SizeU ];
|
|
return TexData;
|
|
}
|
|
|
|
uint16 FLandscapeEditDataInterface::GetHeightMapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData /*= NULL*/)
|
|
{
|
|
const FColor& TexData = GetHeightMapColor(Component, TexU, TexV, TextureData);
|
|
return ((((uint16)TexData.R) << 8) | TexData.G);
|
|
}
|
|
|
|
uint16 FLandscapeEditDataInterface::GetHeightMapAlphaBlendData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData /*= NULL*/)
|
|
{
|
|
const FColor& TexData = GetHeightMapColor(Component, TexU, TexV, TextureData);
|
|
return ((((uint16)TexData.B) << 8) | TexData.A) & 0xFFFC;
|
|
}
|
|
|
|
uint8 FLandscapeEditDataInterface::GetHeightMapFlagsData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData /*= NULL*/)
|
|
{
|
|
const FColor& TexData = GetHeightMapColor(Component, TexU, TexV, TextureData);
|
|
return TexData.A & 0x3;
|
|
}
|
|
|
|
template<typename TDataAccess, typename TGetHeightMapDataFunction>
|
|
void FLandscapeEditDataInterface::GetHeightDataInternal(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TDataAccess& StoreData, TGetHeightMapDataFunction GetHeightMapDataFunction)
|
|
{
|
|
// Copy variables
|
|
int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2;
|
|
ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN;
|
|
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
int32 ComponentSizeX = ComponentIndexX2-ComponentIndexX1+1;
|
|
int32 ComponentSizeY = ComponentIndexY2-ComponentIndexY1+1;
|
|
|
|
// Neighbor Components
|
|
ULandscapeComponent* BorderComponent[4] = {0, 0, 0, 0};
|
|
ULandscapeComponent* CornerComponent[4] = {0, 0, 0, 0};
|
|
bool NoBorderX1 = false, NoBorderX2 = false;
|
|
TArray<bool> NoBorderY1, NoBorderY2, ComponentDataExist;
|
|
TArray<ULandscapeComponent*> BorderComponentY1, BorderComponentY2;
|
|
ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY);
|
|
ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY);
|
|
bool bHasMissingValue = false;
|
|
|
|
FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = {0, 0, 0, 0};
|
|
FColor* NeighborHeightmapTextureData[4] = {0, 0, 0, 0};
|
|
typename TDataAccess::DataType CornerValues[4] = {0, 0, 0, 0};
|
|
|
|
int32 EdgeCoord = (SubsectionSizeQuads+1) * ComponentNumSubsections - 1; //ComponentSizeQuads;
|
|
|
|
// initial loop....
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
NoBorderX1 = false;
|
|
NoBorderX2 = false;
|
|
BorderComponent[0] = BorderComponent[1] = NULL;
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
BorderComponent[2] = BorderComponent[3] = NULL;
|
|
int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1;
|
|
int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1;
|
|
int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1;
|
|
ComponentDataExist[ComponentIndexXY] = false;
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY));
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
FColor* HeightmapTextureData = NULL;
|
|
uint8 CornerSet = 0;
|
|
bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ];
|
|
bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ];
|
|
|
|
if( Component )
|
|
{
|
|
TexDataInfo = GetTextureDataInfo(Component->GetHeightmap(GetEditLayer()));
|
|
HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
ComponentDataExist[ComponentIndexXY] = true;
|
|
// Update valid region
|
|
ValidX1 = FMath::Min<int32>(Component->GetSectionBase().X, ValidX1);
|
|
ValidX2 = FMath::Max<int32>(Component->GetSectionBase().X+ComponentSizeQuads, ValidX2);
|
|
ValidY1 = FMath::Min<int32>(Component->GetSectionBase().Y, ValidY1);
|
|
ValidY2 = FMath::Max<int32>(Component->GetSectionBase().Y+ComponentSizeQuads, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
if (!bHasMissingValue)
|
|
{
|
|
NoBorderY1.Empty(ComponentSizeX);
|
|
NoBorderY2.Empty(ComponentSizeX);
|
|
NoBorderY1.AddZeroed(ComponentSizeX);
|
|
NoBorderY2.AddZeroed(ComponentSizeX);
|
|
BorderComponentY1.Empty(ComponentSizeX);
|
|
BorderComponentY2.Empty(ComponentSizeX);
|
|
BorderComponentY1.AddZeroed(ComponentSizeX);
|
|
BorderComponentY2.AddZeroed(ComponentSizeX);
|
|
bHasMissingValue = true;
|
|
}
|
|
|
|
// Search for neighbor component for interpolation
|
|
bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX);
|
|
bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY);
|
|
// Search for left-closest component
|
|
if ( bShouldSearchX || (!NoBorderX1 && !BorderComponent[0]))
|
|
{
|
|
NoBorderX1 = true;
|
|
for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--)
|
|
{
|
|
BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY));
|
|
if (BorderComponent[0])
|
|
{
|
|
NoBorderX1 = false;
|
|
NeighborTexDataInfo[0] = GetTextureDataInfo(BorderComponent[0]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[0] = (FColor*)NeighborTexDataInfo[0]->GetMipData(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for right-closest component
|
|
if ( bShouldSearchX || (!NoBorderX2 && !BorderComponent[1]))
|
|
{
|
|
NoBorderX2 = true;
|
|
for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++)
|
|
{
|
|
BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY));
|
|
if (BorderComponent[1])
|
|
{
|
|
NoBorderX2 = false;
|
|
NeighborTexDataInfo[1] = GetTextureDataInfo(BorderComponent[1]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[1] = (FColor*)NeighborTexDataInfo[1]->GetMipData(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for up-closest component
|
|
if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX]))
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--)
|
|
{
|
|
BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y));
|
|
if (BorderComponent[2])
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = false;
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[2] = BorderComponentY1[ComponentIndexXX];
|
|
if (BorderComponent[2])
|
|
{
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
}
|
|
}
|
|
// Search for bottom-closest component
|
|
if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX]))
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++)
|
|
{
|
|
BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y));
|
|
if (BorderComponent[3])
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = false;
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[3] = BorderComponentY2[ComponentIndexXX];
|
|
if (BorderComponent[3])
|
|
{
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->GetHeightmap(GetEditLayer()));
|
|
NeighborHeightmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
}
|
|
}
|
|
|
|
CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY-1))) : NULL;
|
|
CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY-1))) : NULL;
|
|
CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY+1))) : NULL;
|
|
CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY+1))) : NULL;
|
|
|
|
if (CornerComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetHeightMapDataFunction(CornerComponent[0], EdgeCoord, EdgeCoord, nullptr);
|
|
}
|
|
else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads )
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = StoreData.Load(ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetHeightMapDataFunction(BorderComponent[0], EdgeCoord, 0, NeighborHeightmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetHeightMapDataFunction(BorderComponent[2], 0, EdgeCoord, NeighborHeightmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetHeightMapDataFunction(CornerComponent[1], 0, EdgeCoord, nullptr);
|
|
}
|
|
else if (ExistUp && X2 >= (ComponentIndexX+1)*ComponentSizeQuads)
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = StoreData.Load( (ComponentIndexX+1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetHeightMapDataFunction(BorderComponent[1], 0, 0, NeighborHeightmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetHeightMapDataFunction(BorderComponent[2], EdgeCoord, EdgeCoord, NeighborHeightmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[2])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetHeightMapDataFunction(CornerComponent[2], EdgeCoord, 0, nullptr);
|
|
}
|
|
else if (ExistLeft && Y2 >= (ComponentIndexY+1)*ComponentSizeQuads) // Use data already stored for 0, 2
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = StoreData.Load(ComponentIndexX*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetHeightMapDataFunction(BorderComponent[0], EdgeCoord, EdgeCoord, NeighborHeightmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetHeightMapDataFunction(BorderComponent[3], 0, 0, NeighborHeightmapTextureData[3]);
|
|
}
|
|
|
|
if (CornerComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetHeightMapDataFunction(CornerComponent[3], 0, 0, nullptr);
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetHeightMapDataFunction(BorderComponent[1], 0, EdgeCoord, NeighborHeightmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetHeightMapDataFunction(BorderComponent[3], EdgeCoord, 0, NeighborHeightmapTextureData[3]);
|
|
}
|
|
|
|
FillCornerValues(CornerSet, CornerValues);
|
|
ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet;
|
|
}
|
|
|
|
if (!ComponentDataExist[ComponentIndexXY])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
// Find the input data corresponding to this vertex
|
|
if( Component )
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
typename TDataAccess::DataType Height = GetHeightMapDataFunction(Component, (SubsectionSizeQuads + 1) * SubIndexX + SubX, (SubsectionSizeQuads + 1) * SubIndexY + SubY, HeightmapTextureData);
|
|
StoreData.Store(LandscapeX, LandscapeY, Height);
|
|
}
|
|
else
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
typename TDataAccess::DataType Value[4] = {0, 0, 0, 0};
|
|
int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX};
|
|
float ValueX = 0.0f, ValueY = 0.0f;
|
|
bool Exist[4] = {false, false, false, false};
|
|
|
|
// Use data already stored for 0, 2
|
|
if (ExistLeft)
|
|
{
|
|
Value[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, LandscapeY);
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
Value[0] = GetHeightMapDataFunction(BorderComponent[0], EdgeCoord, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborHeightmapTextureData[0]);
|
|
Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 2))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[0] = static_cast<typename TDataAccess::DataType>((float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2));
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[1])
|
|
{
|
|
Value[1] = GetHeightMapDataFunction(BorderComponent[1], 0, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborHeightmapTextureData[1]);
|
|
Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[1] = static_cast<typename TDataAccess::DataType>((float)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
}
|
|
|
|
if (ExistUp)
|
|
{
|
|
Value[2] = StoreData.Load(LandscapeX, ComponentIndexY*ComponentSizeQuads);
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
Value[2] = GetHeightMapDataFunction(BorderComponent[2], (SubsectionSizeQuads + 1) * SubIndexX + SubX, EdgeCoord, NeighborHeightmapTextureData[2]);
|
|
Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 1))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[2] = static_cast<typename TDataAccess::DataType>((float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2));
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[3])
|
|
{
|
|
Value[3] = GetHeightMapDataFunction(BorderComponent[3], (SubsectionSizeQuads + 1) * SubIndexX + SubX, 0, NeighborHeightmapTextureData[3]);
|
|
Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[3] = static_cast<typename TDataAccess::DataType>((float)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
}
|
|
|
|
CalcInterpValue<typename TDataAccess::DataType>(Dist, Exist, Value, ValueX, ValueY);
|
|
|
|
typename TDataAccess::DataType FinalValue = 0; // Default Value
|
|
if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = CalcValueFromValueXY<typename TDataAccess::DataType>(Dist, ValueX, ValueY, CornerSet, CornerValues);
|
|
}
|
|
else if ( (BorderComponent[0] || BorderComponent[1]) )
|
|
{
|
|
FinalValue = static_cast<typename TDataAccess::DataType>(ValueX);
|
|
}
|
|
else if ( (BorderComponent[2] || BorderComponent[3]) )
|
|
{
|
|
FinalValue = static_cast<typename TDataAccess::DataType>(ValueY);
|
|
}
|
|
else if ( (Exist[0] || Exist[1]) )
|
|
{
|
|
FinalValue = static_cast<typename TDataAccess::DataType>(ValueX);
|
|
}
|
|
else if ( (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = static_cast<typename TDataAccess::DataType>(ValueY);
|
|
}
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, FinalValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasMissingValue)
|
|
{
|
|
// generate something to fill in any missing data within the requested region
|
|
CalcMissingValues<typename TDataAccess::DataType, TDataAccess, float>(X1, X2, Y1, Y2,
|
|
ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2,
|
|
ComponentSizeX, ComponentSizeY, CornerValues,
|
|
NoBorderY1, NoBorderY2, ComponentDataExist, StoreData);
|
|
|
|
// clamp the returned bounds to the original request bounds
|
|
// this means the returned bounds are allowed to shrink (if there is only a smaller part of the request that actually exists)
|
|
// but it is not allowed to expand beyond the original request
|
|
ValidX1 = FMath::Max<int32>(X1, ValidX1);
|
|
ValidX2 = FMath::Min<int32>(X2, ValidX2);
|
|
ValidY1 = FMath::Max<int32>(Y1, ValidY1);
|
|
ValidY2 = FMath::Min<int32>(Y2, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
// if there were no missing values, then the entire requested bounds is returned
|
|
ValidX1 = X1;
|
|
ValidX2 = X2;
|
|
ValidY1 = Y1;
|
|
ValidY2 = Y2;
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
template<typename T>
|
|
struct TArrayDataAccess
|
|
{
|
|
typedef T DataType;
|
|
int32 X1;
|
|
int32 Y1;
|
|
DataType* Data;
|
|
int32 Stride;
|
|
|
|
TArrayDataAccess(int32 InX1, int32 InY1, DataType* InData, int32 InStride)
|
|
: X1(InX1)
|
|
, Y1(InY1)
|
|
, Data(InData)
|
|
, Stride(InStride)
|
|
{}
|
|
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, DataType NewValue)
|
|
{
|
|
Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = NewValue;
|
|
}
|
|
|
|
inline DataType Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ];
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct TSparseDataAccess
|
|
{
|
|
typedef T DataType;
|
|
TMap<FIntPoint, DataType>& SparseData;
|
|
|
|
TSparseDataAccess(TMap<FIntPoint, DataType>& InSparseData)
|
|
: SparseData(InSparseData)
|
|
{}
|
|
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, DataType NewValue)
|
|
{
|
|
SparseData.Add(FIntPoint(LandscapeX,LandscapeY), NewValue);
|
|
}
|
|
|
|
inline DataType Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return SparseData.FindRef(FIntPoint(LandscapeX,LandscapeY));
|
|
}
|
|
};
|
|
};
|
|
|
|
template<typename TDataAccess>
|
|
void FLandscapeEditDataInterface::GetHeightDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TDataAccess& StoreData)
|
|
{
|
|
GetHeightDataInternal(ValidX1, ValidY1, ValidX2, ValidY2, StoreData, [&](const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData)->uint16 { return GetHeightMapData(Component, TexU, TexV, TextureData); });
|
|
}
|
|
|
|
template<typename TDataAccess>
|
|
void FLandscapeEditDataInterface::GetHeightAlphaBlendDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TDataAccess& StoreData)
|
|
{
|
|
GetHeightDataInternal(ValidX1, ValidY1, ValidX2, ValidY2, StoreData, [&](const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData)->uint16 { return GetHeightMapAlphaBlendData(Component, TexU, TexV, TextureData); });
|
|
}
|
|
|
|
template<typename TDataAccess>
|
|
void FLandscapeEditDataInterface::GetHeightFlagsDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TDataAccess& StoreData)
|
|
{
|
|
GetHeightDataInternal(ValidX1, ValidY1, ValidX2, ValidY2, StoreData, [&](const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData)->uint8 { return GetHeightMapFlagsData(Component, TexU, TexV, TextureData); });
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint16* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
|
|
TArrayDataAccess<uint16> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetHeightDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightAlphaBlendData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint16* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
|
|
TArrayDataAccess<uint16> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetHeightAlphaBlendDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightFlagsData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
|
|
TArrayDataAccess<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetHeightFlagsDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint16* Data, int32 Stride, uint16* NormalData /*= NULL*/, UTexture2D* InHeightmap)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
|
|
TArrayDataAccess<uint16> ArrayStoreData(X1, Y1, Data, Stride);
|
|
if (NormalData)
|
|
{
|
|
TArrayDataAccess<uint16> ArrayNormalData(X1, Y1, NormalData, Stride);
|
|
GetHeightDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData, InHeightmap, &ArrayNormalData);
|
|
}
|
|
else
|
|
{
|
|
GetHeightDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData, InHeightmap);
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint16>& SparseData)
|
|
{
|
|
TSparseDataAccess<uint16> SparseStoreData(SparseData);
|
|
GetHeightDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint16>& SparseData, TMap<FIntPoint, uint16>* NormalData /*= NULL*/, UTexture2D* InHeightmap)
|
|
{
|
|
TSparseDataAccess<uint16> SparseStoreData(SparseData);
|
|
if (NormalData)
|
|
{
|
|
TSparseDataAccess<uint16> SparseNormalData(*NormalData);
|
|
GetHeightDataTemplFast(X1, Y1, X2, Y2, SparseStoreData, InHeightmap, &SparseNormalData);
|
|
}
|
|
else
|
|
{
|
|
GetHeightDataTemplFast(X1, Y1, X2, Y2, SparseStoreData, InHeightmap);
|
|
}
|
|
}
|
|
|
|
bool ULandscapeComponent::DeleteLayerIfAllZero(const FGuid& InEditLayerGuid, const uint8* const TexDataPtr, int32 TexSize, int32 LayerIdx, bool bShouldDirtyPackage)
|
|
{
|
|
if (TexDataPtr == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the data for the entire component and to see if it's all zero
|
|
for (int32 TexY = 0; TexY < TexSize; TexY++)
|
|
{
|
|
for (int32 TexX = 0; TexX < TexSize; TexX++)
|
|
{
|
|
const int32 TexDataIndex = 4 * (TexX + TexY * TexSize);
|
|
|
|
// Stop the first time we see any non-zero data
|
|
uint8 Weight = TexDataPtr[TexDataIndex];
|
|
if (Weight != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
DeleteLayerAllocation(InEditLayerGuid, LayerIdx, bShouldDirtyPackage);
|
|
MarkRenderStateDirty();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLandscapeEditDataInterface& LandscapeEdit)
|
|
{
|
|
FGuid EditLayerGuid = this->GetEditingLayerGUID();
|
|
DeleteLayerInternal(LayerInfo, LandscapeEdit, EditLayerGuid);
|
|
}
|
|
|
|
void ULandscapeComponent::DeleteLayerInternal(ULandscapeLayerInfoObject* LayerInfo, FLandscapeEditDataInterface& LandscapeEdit, const FGuid& InEditLayerGuid)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(LandscapeComponent_DeleteLayer);
|
|
|
|
ULandscapeComponent* Component = this;
|
|
|
|
// This can be called during WeightmapFixup, so skip checking for updated weightmap allocations
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(InEditLayerGuid);
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(InEditLayerGuid);
|
|
|
|
// Delete the first LayerAllocation with a matching LayerInfo
|
|
const int32 DeleteAllocIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate(
|
|
[LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; });
|
|
if (DeleteAllocIdx == INDEX_NONE)
|
|
{
|
|
// Layer not used for this component.
|
|
return;
|
|
}
|
|
|
|
Component->DeleteLayerAllocation(InEditLayerGuid, DeleteAllocIdx, LandscapeEdit.GetShouldDirtyPackage());
|
|
|
|
// See if the deleted layer is a NoWeightBlend layer - if not, we don't have to worry about normalization
|
|
// If the layer doesn't exist, assume it is a blended layer (so renormalization will run whether needed or not)
|
|
bool bDeleteLayerIsNoWeightBlend = (LayerInfo && LayerInfo->bNoWeightBlend);
|
|
|
|
if (!bDeleteLayerIsNoWeightBlend)
|
|
{
|
|
// Lock data for all the weightmaps
|
|
TArray<FLandscapeTextureDataInfo*> TexDataInfos;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
TexDataInfos.Add(LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]));
|
|
}
|
|
|
|
TArray<bool> LayerNoWeightBlends; // Array of NoWeightBlend flags
|
|
TArray<uint8*> LayerDataPtrs; // Pointers to all layers' data
|
|
|
|
// Get the data for each layer
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
LayerDataPtrs.Add((uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]);
|
|
|
|
// Find the layer info and record if it is a bNoWeightBlend layer.
|
|
LayerNoWeightBlends.Add(Allocation.LayerInfo && Allocation.LayerInfo->bNoWeightBlend);
|
|
}
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
const int32 SizeU = (SubsectionSizeQuads + 1) * NumSubsections;
|
|
const int32 SizeV = (SubsectionSizeQuads + 1) * NumSubsections;
|
|
const int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
const int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
for (int32 SubIndexY = 0; SubIndexY < NumSubsections; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = 0; SubIndexX < NumSubsections; SubIndexX++)
|
|
{
|
|
for (int32 SubY = 0; SubY <= SubsectionSizeQuads; SubY++)
|
|
{
|
|
for (int32 SubX = 0; SubX <= SubsectionSizeQuads; SubX++)
|
|
{
|
|
const int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX;
|
|
const int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY;
|
|
const int32 TexDataIndex = 4 * (TexX + TexY * SizeU);
|
|
|
|
// Calculate the sum of other layer weights
|
|
int32 OtherLayerWeightSum = 0;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerIdx != DeleteAllocIdx && LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
OtherLayerWeightSum += LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
}
|
|
}
|
|
|
|
if (OtherLayerWeightSum == 0)
|
|
{
|
|
// Set the first other weight-blend layer we can find to 255 to avoid a black hole
|
|
// This isn't ideal but it's the best option
|
|
// There's nothing we can easily do if this was the only weight-blend layer on this component
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerIdx != DeleteAllocIdx && LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
Weight = 255;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Adjust other layer weights
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerIdx != DeleteAllocIdx && LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
Weight = static_cast<uint8>(FMath::Clamp<int32>(FMath::RoundToInt32(255.0f * (float)Weight / (float)OtherLayerWeightSum), 0, 255));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update all the textures and mips
|
|
for (int32 Idx = 0; Idx < ComponentWeightmapTextures.Num(); Idx++)
|
|
{
|
|
UTexture2D* WeightmapTexture = ComponentWeightmapTextures[Idx];
|
|
FLandscapeTextureDataInfo* WeightmapDataInfo = TexDataInfos[Idx];
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx);
|
|
}
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(Component->NumSubsections, Component->SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo);
|
|
|
|
WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1);
|
|
}
|
|
}
|
|
|
|
// Update the shaders for this component
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
|
|
// Update dominant layer info stored in collision component
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
TArray<FColor*> CollisionWeightmapMipData;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel));
|
|
}
|
|
TArray<FColor*> SimpleCollisionWeightmapMipData;
|
|
if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel)
|
|
{
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel));
|
|
}
|
|
}
|
|
Component->UpdateCollisionLayerData(
|
|
CollisionWeightmapMipData.GetData(),
|
|
Component->SimpleCollisionMipLevel > Component->CollisionMipLevel ? SimpleCollisionWeightmapMipData.GetData() : nullptr);
|
|
|
|
ULandscapeHeightfieldCollisionComponent* LocalCollisionComponent = Component->GetCollisionComponent();
|
|
if (LocalCollisionComponent)
|
|
{
|
|
LocalCollisionComponent->RecreateCollision();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo)
|
|
{
|
|
if (!LandscapeInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto& XYComponentPair : LandscapeInfo->XYtoComponentMap)
|
|
{
|
|
ULandscapeComponent* Component = XYComponentPair.Value;
|
|
Component->DeleteLayerInternal(LayerInfo, *this, GetEditLayer());
|
|
}
|
|
|
|
if (LandscapeInfo->LandscapeActor.IsValid() && LandscapeInfo->LandscapeActor->HasLayersContent())
|
|
{
|
|
LandscapeInfo->LandscapeActor->RequestLayersContentUpdate(ELandscapeLayerUpdateMode::Update_All);
|
|
}
|
|
else
|
|
{
|
|
TSet<ULandscapeComponent*> Components;
|
|
Algo::Transform(LandscapeInfo->XYtoComponentMap, Components, &TPair<FIntPoint, ULandscapeComponent*>::Value);
|
|
ALandscapeProxy::InvalidateGeneratedComponentData(Components);
|
|
}
|
|
}
|
|
|
|
void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLandscapeEditDataInterface& LandscapeEdit)
|
|
{
|
|
check(LayerInfo);
|
|
|
|
ULandscapeComponent* Component = this;
|
|
|
|
FGuid EditLayerGuid = LandscapeEdit.GetEditLayer();
|
|
|
|
const bool bIsFinalWeightmap = !EditLayerGuid.IsValid();
|
|
|
|
ALandscapeProxy* Proxy = Component->GetLandscapeProxy();
|
|
Component->Modify(LandscapeEdit.GetShouldDirtyPackage());
|
|
Proxy->Modify(LandscapeEdit.GetShouldDirtyPackage());
|
|
|
|
const bool bFillLayerIsNoWeightBlend = LayerInfo->bNoWeightBlend;
|
|
bool bClearOtherWeightBlendLayers = !bFillLayerIsNoWeightBlend;
|
|
|
|
TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(EditLayerGuid);
|
|
TArray<TObjectPtr<UTexture2D>>& ComponentWeightmapTextures = Component->GetWeightmapTextures(EditLayerGuid);
|
|
TArray<TObjectPtr<ULandscapeWeightmapUsage>>& ComponentWeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(EditLayerGuid);
|
|
|
|
// Find the index for this layer in this component.
|
|
int32 FillLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate(
|
|
[LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; });
|
|
|
|
// if the layer isn't used on this component yet but is a weight-blend layer, then simply steal the allocation of another weight-blend layer!
|
|
if (FillLayerIdx == INDEX_NONE && !bFillLayerIsNoWeightBlend)
|
|
{
|
|
FillLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate(
|
|
[](const FWeightmapLayerAllocationInfo& Allocation) { return !Allocation.LayerInfo || !Allocation.LayerInfo->bNoWeightBlend; });
|
|
|
|
if (FillLayerIdx != INDEX_NONE)
|
|
{
|
|
ComponentWeightmapLayerAllocations[FillLayerIdx].LayerInfo = LayerInfo;
|
|
}
|
|
else
|
|
{
|
|
// no other weight-blend layers exist
|
|
bClearOtherWeightBlendLayers = false;
|
|
}
|
|
}
|
|
|
|
// if the layer is still not found then we are forced to make a new allocation
|
|
if (FillLayerIdx == INDEX_NONE)
|
|
{
|
|
FillLayerIdx = ComponentWeightmapLayerAllocations.Num();
|
|
ComponentWeightmapLayerAllocations.Add(FWeightmapLayerAllocationInfo(LayerInfo));
|
|
Component->ReallocateWeightmaps(/*DataInterface =*/&LandscapeEdit, EditLayerGuid, /*bInSaveToTransactionBuffer = */true, /*bool bInForceReallocate = */false, /*InTargetProxy = */nullptr, /*InRestrictSharingToComponents = */nullptr);
|
|
}
|
|
|
|
check(FillLayerIdx != INDEX_NONE);
|
|
|
|
// fill the layer
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
const int32 SizeU = (SubsectionSizeQuads + 1) * NumSubsections;
|
|
const int32 SizeV = (SubsectionSizeQuads + 1) * NumSubsections;
|
|
const int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
const int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
FWeightmapLayerAllocationInfo& FillLayerAllocation = ComponentWeightmapLayerAllocations[FillLayerIdx];
|
|
uint8* MaterialLayerData = (uint8*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[FillLayerAllocation.WeightmapTextureIndex])->GetMipData(0);
|
|
|
|
for (int32 Y = 0; Y < SizeV; ++Y)
|
|
{
|
|
const int32 TexY = WeightmapOffsetY + Y;
|
|
uint8* RowData = MaterialLayerData + ((WeightmapOffsetY + Y) * SizeU + WeightmapOffsetX) * 4 + ChannelOffsets[FillLayerAllocation.WeightmapTextureChannel];
|
|
for (int32 X = 0; X < SizeU; ++X)
|
|
{
|
|
RowData[X * 4] = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
// clear other layers
|
|
if (bClearOtherWeightBlendLayers)
|
|
{
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); ++LayerIdx)
|
|
{
|
|
if (LayerIdx == FillLayerIdx)
|
|
{
|
|
continue;
|
|
}
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
if ((Allocation.LayerInfo == nullptr) || Allocation.LayerInfo->bNoWeightBlend)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[Allocation.WeightmapTextureIndex];
|
|
if (Usage) // can be null if WeightmapUsageMap hasn't been built yet
|
|
{
|
|
Usage->ChannelUsage[Allocation.WeightmapTextureChannel] = nullptr;
|
|
}
|
|
|
|
Allocation.Free();
|
|
}
|
|
|
|
ComponentWeightmapLayerAllocations.RemoveAll(
|
|
[](const FWeightmapLayerAllocationInfo& Allocation) { return !Allocation.IsAllocated(); });
|
|
|
|
// remove any textures we're no longer using
|
|
for (int32 TextureIdx = 0; TextureIdx < ComponentWeightmapTextures.Num(); ++TextureIdx)
|
|
{
|
|
if (!ComponentWeightmapLayerAllocations.ContainsByPredicate(
|
|
[TextureIdx](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.WeightmapTextureIndex == TextureIdx; }))
|
|
{
|
|
ComponentWeightmapTextures[TextureIdx]->Modify(LandscapeEdit.GetShouldDirtyPackage());
|
|
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); ++LayerIdx)
|
|
{
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
if (Allocation.WeightmapTextureIndex > TextureIdx)
|
|
{
|
|
--Allocation.WeightmapTextureIndex;
|
|
}
|
|
}
|
|
|
|
ComponentWeightmapTextures.RemoveAt(TextureIdx);
|
|
if (ComponentWeightmapTexturesUsage.IsValidIndex(TextureIdx))
|
|
{
|
|
ComponentWeightmapTexturesUsage.RemoveAt(TextureIdx);
|
|
}
|
|
|
|
TextureIdx--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// todo - normalize texture usage: it's possible to end up with two textures each using one channel at this point
|
|
// e.g. if you start with 4 blended layers (in one texture) and a non-blended layer (in a 2nd), and fill one weight-blended layer (deleting the other three)
|
|
// this can also happen with normal painting I believe
|
|
|
|
// update mips if this texture isn't used in the layer compositing
|
|
if (bIsFinalWeightmap)
|
|
{
|
|
for (int32 TextureIdx = 0; TextureIdx < ComponentWeightmapTextures.Num(); ++TextureIdx)
|
|
{
|
|
UTexture2D* WeightmapTexture = ComponentWeightmapTextures[TextureIdx];
|
|
FLandscapeTextureDataInfo* WeightmapDataInfo = LandscapeEdit.GetTextureDataInfo(WeightmapTexture);
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx);
|
|
}
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo);
|
|
|
|
WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1);
|
|
}
|
|
}
|
|
|
|
// Update the shaders for this component
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->InvalidateLightingCache();
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
|
|
// Update dominant layer info stored in collision component
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
TArray<FColor*> CollisionWeightmapMipData;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel));
|
|
}
|
|
TArray<FColor*> SimpleCollisionWeightmapMipData;
|
|
if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel)
|
|
{
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel));
|
|
}
|
|
}
|
|
Component->UpdateCollisionLayerData(
|
|
CollisionWeightmapMipData.GetData(),
|
|
Component->SimpleCollisionMipLevel > Component->CollisionMipLevel ? SimpleCollisionWeightmapMipData.GetData() : nullptr);
|
|
|
|
ULandscapeHeightfieldCollisionComponent* LocalCollisionComponent = Component->GetCollisionComponent();
|
|
if (LocalCollisionComponent)
|
|
{
|
|
LocalCollisionComponent->RecreateCollision();
|
|
}
|
|
}
|
|
|
|
Proxy->ValidateProxyLayersWeightmapUsage();
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::FillLayer(ULandscapeLayerInfoObject* LayerInfo)
|
|
{
|
|
const bool bEmptyLayersOnly = false;
|
|
FillLayer(LayerInfo, bEmptyLayersOnly);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::FillEmptyLayers(ULandscapeLayerInfoObject* LayerInfo)
|
|
{
|
|
const bool bEmptyLayersOnly = true;
|
|
FillLayer(LayerInfo, bEmptyLayersOnly);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::FillLayer(ULandscapeLayerInfoObject* LayerInfo, bool bEmptyLayersOnly)
|
|
{
|
|
if (!LandscapeInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LayerInfo->IsReferencedFromLoadedData = true;
|
|
|
|
for (auto& XYComponentPair : LandscapeInfo->XYtoComponentMap)
|
|
{
|
|
ULandscapeComponent* Component = XYComponentPair.Value;
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
|
|
bool LayerNeedFilling = true;
|
|
|
|
if (bEmptyLayersOnly)
|
|
{
|
|
for (const FWeightmapLayerAllocationInfo& Alloc : ComponentWeightmapLayerAllocations)
|
|
{
|
|
if (Alloc.LayerInfo != nullptr)
|
|
{
|
|
LayerNeedFilling = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LayerNeedFilling)
|
|
{
|
|
Component->FillLayer(LayerInfo, *this);
|
|
}
|
|
}
|
|
|
|
if (LandscapeInfo->LandscapeActor.IsValid() && LandscapeInfo->LandscapeActor->HasLayersContent())
|
|
{
|
|
LandscapeInfo->LandscapeActor->RequestLayersContentUpdateForceAll();
|
|
}
|
|
else
|
|
{
|
|
TSet<ULandscapeComponent*> Components;
|
|
Algo::Transform(LandscapeInfo->XYtoComponentMap, Components, &TPair<FIntPoint, ULandscapeComponent*>::Value);
|
|
ALandscapeProxy::InvalidateGeneratedComponentData(Components);
|
|
}
|
|
}
|
|
|
|
void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo, FLandscapeEditDataInterface& LandscapeEdit)
|
|
{
|
|
ReplaceLayerInternal(FromLayerInfo, ToLayerInfo, LandscapeEdit, GetEditingLayerGUID());
|
|
}
|
|
|
|
void ULandscapeComponent::ReplaceLayerInternal(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo, FLandscapeEditDataInterface& LandscapeEdit, const FGuid& InEditLayerGUID)
|
|
{
|
|
check(FromLayerInfo); // ToLayerInfo may be null when clearing a layer
|
|
if (FromLayerInfo == ToLayerInfo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(InEditLayerGUID);
|
|
TArray<TObjectPtr<UTexture2D>>& ComponentWeightmapTextures = GetWeightmapTextures(InEditLayerGUID);
|
|
TArray<TObjectPtr<ULandscapeWeightmapUsage>>& ComponentWeightmapTexturesUsage = GetWeightmapTexturesUsage(InEditLayerGUID);
|
|
|
|
// Find the index for this layer in this component.
|
|
int32 FromLayerIdx = INDEX_NONE;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
if (Allocation.LayerInfo == FromLayerInfo)
|
|
{
|
|
FromLayerIdx = LayerIdx;
|
|
}
|
|
}
|
|
if (FromLayerIdx == INDEX_NONE)
|
|
{
|
|
// Layer not used for this component, nothing to do.
|
|
return;
|
|
}
|
|
|
|
bool bMerging = true;
|
|
|
|
// Find the index for this layer in this component.
|
|
int32 ToLayerIdx = INDEX_NONE;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
if (Allocation.LayerInfo == ToLayerInfo)
|
|
{
|
|
ToLayerIdx = LayerIdx;
|
|
}
|
|
}
|
|
if (ToLayerIdx == INDEX_NONE)
|
|
{
|
|
// Layer not used for this component, so do trivial replace.
|
|
ComponentWeightmapLayerAllocations[FromLayerIdx].LayerInfo = ToLayerInfo;
|
|
bMerging = false;
|
|
}
|
|
|
|
FWeightmapLayerAllocationInfo& FromLayerAllocation = ComponentWeightmapLayerAllocations[FromLayerIdx];
|
|
|
|
// See if we'll be able to remove the texture completely.
|
|
bool bCanRemoveLayerTexture = false;
|
|
if (bMerging)
|
|
{
|
|
bCanRemoveLayerTexture = true;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
|
|
// check if we will be able to remove the texture also
|
|
if (LayerIdx != FromLayerIdx && Allocation.WeightmapTextureIndex == FromLayerAllocation.WeightmapTextureIndex)
|
|
{
|
|
bCanRemoveLayerTexture = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if the deleted layer is a NoWeightBlend layer - if not, we don't have to worry about normalization
|
|
const bool bFromLayerIsNoWeightBlend = (FromLayerInfo && FromLayerInfo->bNoWeightBlend);
|
|
const bool bToLayerIsNoWeightBlend = (ToLayerInfo && ToLayerInfo->bNoWeightBlend);
|
|
|
|
const bool bRequireNormalization = (bFromLayerIsNoWeightBlend != bToLayerIsNoWeightBlend);
|
|
|
|
if (bMerging)
|
|
{
|
|
FWeightmapLayerAllocationInfo& ToLayerAllocation = ComponentWeightmapLayerAllocations[ToLayerIdx];
|
|
|
|
// Lock data for all the weightmaps
|
|
FLandscapeTextureDataInfo* FromTexDataInfo = LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]);
|
|
FLandscapeTextureDataInfo* ToTexDataInfo = LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]);
|
|
|
|
check(FromTexDataInfo->GetMipSizeX(0) == FromTexDataInfo->GetMipSizeY(0));
|
|
check(ToTexDataInfo->GetMipSizeX(0) == ToTexDataInfo->GetMipSizeY(0));
|
|
check(FromTexDataInfo->GetMipSizeX(0) == ToTexDataInfo->GetMipSizeX(0));
|
|
const int32 MipSize = FromTexDataInfo->GetMipSizeX(0);
|
|
|
|
uint8* const SrcTextureData = (uint8*)FromTexDataInfo->GetMipData(0) + ChannelOffsets[FromLayerAllocation.WeightmapTextureChannel];
|
|
uint8* const DestTextureData = (uint8*)ToTexDataInfo->GetMipData(0) + ChannelOffsets[ToLayerAllocation.WeightmapTextureChannel];
|
|
|
|
for (int32 i = 0; i < FMath::Square(MipSize); i++)
|
|
{
|
|
DestTextureData[i*4] = static_cast<uint8>(FMath::Min(255, (uint16)DestTextureData[i*4] + (uint16)SrcTextureData[i*4]));
|
|
}
|
|
|
|
// Update all mips
|
|
if (!bCanRemoveLayerTexture)
|
|
{
|
|
UTexture2D* WeightmapTexture = ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex];
|
|
FLandscapeTextureDataInfo* WeightmapDataInfo = FromTexDataInfo;
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx);
|
|
}
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo);
|
|
|
|
WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1);
|
|
}
|
|
|
|
if (FromTexDataInfo != ToTexDataInfo)
|
|
{
|
|
UTexture2D* WeightmapTexture = ComponentWeightmapTextures[ToLayerAllocation.WeightmapTextureIndex];
|
|
FLandscapeTextureDataInfo* WeightmapDataInfo = ToTexDataInfo;
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx);
|
|
}
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo);
|
|
|
|
WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1);
|
|
}
|
|
}
|
|
|
|
if (bRequireNormalization)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
// if merging into an existing layer, remove the layer and potentially the texture
|
|
if (bMerging)
|
|
{
|
|
ALandscapeProxy* Proxy = GetLandscapeProxy();
|
|
// Mark the channel as unallocated, so we can reuse it later
|
|
ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[FromLayerAllocation.WeightmapTextureIndex];
|
|
//check(Usage);
|
|
if (Usage)
|
|
{
|
|
Usage->ChannelUsage[FromLayerAllocation.WeightmapTextureChannel] = nullptr;
|
|
}
|
|
|
|
// If this layer was the last usage for this texture, we can remove it.
|
|
if (bCanRemoveLayerTexture)
|
|
{
|
|
ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->SetFlags(RF_Transactional);
|
|
ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->Modify(LandscapeEdit.GetShouldDirtyPackage());
|
|
ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->ClearFlags(RF_Standalone);
|
|
|
|
ComponentWeightmapTextures.RemoveAt(FromLayerAllocation.WeightmapTextureIndex);
|
|
ComponentWeightmapTexturesUsage.RemoveAt(FromLayerAllocation.WeightmapTextureIndex);
|
|
|
|
// Adjust WeightmapTextureIndex index for other layers
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerIdx == FromLayerIdx)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
|
|
if (Allocation.WeightmapTextureIndex > FromLayerAllocation.WeightmapTextureIndex)
|
|
{
|
|
Allocation.WeightmapTextureIndex--;
|
|
}
|
|
|
|
check(Allocation.WeightmapTextureIndex < ComponentWeightmapTextures.Num());
|
|
}
|
|
}
|
|
|
|
// Remove the layer
|
|
ComponentWeightmapLayerAllocations.RemoveAt(FromLayerIdx);
|
|
|
|
if (!GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
// Update the shaders for this component
|
|
UpdateMaterialInstances();
|
|
|
|
EditToolRenderData.UpdateDebugColorMaterial(this);
|
|
UpdateEditToolRenderData();
|
|
}
|
|
}
|
|
|
|
RequestWeightmapUpdate();
|
|
|
|
if (!LandscapeEdit.HasLandscapeLayersContent())
|
|
{
|
|
// Update dominant layer info stored in collision component
|
|
TArray<FColor*> CollisionWeightmapMipData;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(CollisionMipLevel));
|
|
}
|
|
TArray<FColor*> SimpleCollisionWeightmapMipData;
|
|
if (SimpleCollisionMipLevel > CollisionMipLevel)
|
|
{
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(SimpleCollisionMipLevel));
|
|
}
|
|
}
|
|
UpdateCollisionLayerData(
|
|
CollisionWeightmapMipData.GetData(),
|
|
SimpleCollisionMipLevel > CollisionMipLevel ? SimpleCollisionWeightmapMipData.GetData() : nullptr);
|
|
|
|
|
|
if(ULandscapeHeightfieldCollisionComponent* CollisionComp = GetCollisionComponent())
|
|
{
|
|
CollisionComp->RecreateCollision();
|
|
}
|
|
}
|
|
|
|
GetLandscapeProxy()->ValidateProxyLayersWeightmapUsage();
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo)
|
|
{
|
|
if (!LandscapeInfo) return;
|
|
|
|
auto DoReplace = [&](const FGuid& EditLayerGuid)
|
|
{
|
|
for (auto It = LandscapeInfo->XYtoComponentMap.CreateIterator(); It; ++It)
|
|
{
|
|
ULandscapeComponent* Component = It.Value();
|
|
Component->ReplaceLayerInternal(FromLayerInfo, ToLayerInfo, *this, EditLayerGuid);
|
|
}
|
|
};
|
|
|
|
if (LandscapeInfo->LandscapeActor.IsValid() && LandscapeInfo->LandscapeActor->HasLayersContent())
|
|
{
|
|
LandscapeInfo->LandscapeActor->Modify(GetShouldDirtyPackage());
|
|
LandscapeInfo->LandscapeActor->ReplaceLayerSubstractiveBlendStatus(FromLayerInfo, ToLayerInfo, GetShouldDirtyPackage());
|
|
LandscapeInfo->LandscapeActor->ForEachEditLayerConst([DoReplace](const ULandscapeEditLayerBase* CurrentLayer)
|
|
{
|
|
DoReplace(CurrentLayer->GetGuid());
|
|
return true;
|
|
});
|
|
LandscapeInfo->LandscapeActor->RequestLayersContentUpdateForceAll(ELandscapeLayerUpdateMode::Update_Weightmap_All);
|
|
}
|
|
else
|
|
{
|
|
FGuid BaseLayerGuid;
|
|
DoReplace(BaseLayerGuid);
|
|
}
|
|
}
|
|
|
|
// simple classes for the template....
|
|
namespace
|
|
{
|
|
template<typename TDataType>
|
|
struct TArrayStoreData
|
|
{
|
|
int32 X1;
|
|
int32 Y1;
|
|
TDataType* Data;
|
|
int32 Stride;
|
|
int32 ArraySize;
|
|
|
|
TArrayStoreData(int32 InX1, int32 InY1, TDataType* InData, int32 InStride)
|
|
: X1(InX1)
|
|
, Y1(InY1)
|
|
, Data(InData)
|
|
, Stride(InStride)
|
|
, ArraySize(1)
|
|
{}
|
|
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) {}
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) {}
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) {}
|
|
inline TDataType Load(int32 LandscapeX, int32 LandscapeY) { return 0; }
|
|
inline void PreInit(int32 InArraySize) { ArraySize = InArraySize; }
|
|
};
|
|
|
|
template<> void TArrayStoreData<uint8>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight)
|
|
{
|
|
Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = Weight;
|
|
}
|
|
|
|
template<> uint8 TArrayStoreData<uint8>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ];
|
|
}
|
|
|
|
template<> FVector2D TArrayStoreData<FVector2D>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return Data[(LandscapeY - Y1) * Stride + (LandscapeX - X1)];
|
|
}
|
|
|
|
template<> FVector TArrayStoreData<FVector>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return Data[(LandscapeY - Y1) * Stride + (LandscapeX - X1)];
|
|
}
|
|
|
|
template<> void TArrayStoreData<FVector2D>::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset)
|
|
{
|
|
Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = Offset;
|
|
}
|
|
|
|
template<> void TArrayStoreData<FVector>::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset)
|
|
{
|
|
Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = FVector(Offset.X, Offset.Y, 0.0f);
|
|
}
|
|
|
|
// Data items should be initialized with ArraySize
|
|
template<> void TArrayStoreData<TArray<uint8>>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx)
|
|
{
|
|
TArray<uint8>& Value = Data[ ((LandscapeY-Y1) * Stride + (LandscapeX-X1)) ];
|
|
if (Value.Num() != ArraySize)
|
|
{
|
|
Value.Empty(ArraySize);
|
|
Value.AddZeroed(ArraySize);
|
|
}
|
|
Value[LayerIdx] = Weight;
|
|
}
|
|
|
|
template<typename TDataType>
|
|
struct TSparseStoreData
|
|
{
|
|
TMap<FIntPoint, TDataType>& SparseData;
|
|
int32 ArraySize;
|
|
|
|
TSparseStoreData(TMap<FIntPoint, TDataType>& InSparseData)
|
|
: SparseData(InSparseData)
|
|
, ArraySize(1)
|
|
{}
|
|
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) {}
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) {}
|
|
inline void Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) {}
|
|
inline TDataType Load(int32 LandscapeX, int32 LandscapeY) { return 0; }
|
|
inline void PreInit(int32 InArraySize) { ArraySize = InArraySize; }
|
|
};
|
|
|
|
template<> void TSparseStoreData<uint8>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight)
|
|
{
|
|
SparseData.Add(FIntPoint(LandscapeX,LandscapeY), Weight);
|
|
}
|
|
|
|
template<> uint8 TSparseStoreData<uint8>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return SparseData.FindRef(FIntPoint(LandscapeX,LandscapeY));
|
|
}
|
|
|
|
template<> FVector2D TSparseStoreData<FVector2D>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return SparseData.FindRef(FIntPoint(LandscapeX, LandscapeY));
|
|
}
|
|
|
|
template<> FVector TSparseStoreData<FVector>::Load(int32 LandscapeX, int32 LandscapeY)
|
|
{
|
|
return SparseData.FindRef(FIntPoint(LandscapeX, LandscapeY));
|
|
}
|
|
|
|
template<> void TSparseStoreData<TArray<uint8>>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx)
|
|
{
|
|
TArray<uint8>* Value = SparseData.Find(FIntPoint(LandscapeX,LandscapeY));
|
|
if (Value)
|
|
{
|
|
(*Value)[LayerIdx] = Weight;
|
|
}
|
|
else
|
|
{
|
|
TArray<uint8> Values;
|
|
Values.Empty(ArraySize);
|
|
Values.AddZeroed(ArraySize);
|
|
Values[LayerIdx] = Weight;
|
|
SparseData.Add(FIntPoint(LandscapeX, LandscapeY), Values);
|
|
}
|
|
}
|
|
|
|
template<> void TSparseStoreData<FVector2D>::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset)
|
|
{
|
|
SparseData.Add(FIntPoint(LandscapeX,LandscapeY), Offset);
|
|
}
|
|
|
|
template<> void TSparseStoreData<FVector>::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset)
|
|
{
|
|
// Preserve old Z value
|
|
FVector* PrevValue = SparseData.Find(FIntPoint(LandscapeX,LandscapeY));
|
|
if (PrevValue != NULL)
|
|
{
|
|
PrevValue->X = Offset.X;
|
|
PrevValue->Y = Offset.Y;
|
|
}
|
|
else
|
|
{
|
|
SparseData.Add(FIntPoint(LandscapeX,LandscapeY), FVector(Offset.X, Offset.Y, 0.0f));
|
|
}
|
|
}
|
|
};
|
|
|
|
inline bool FLandscapeEditDataInterface::IsLayerAllowed(const ULandscapeLayerInfoObject* const LayerInfo, const int32 ComponentIndexX, const int32 SubIndexX, const int32 SubX, const int32 ComponentIndexY, const int32 SubIndexY, const int32 SubY)
|
|
{
|
|
// left / right
|
|
if (SubIndexX == 0 && SubX == 0)
|
|
{
|
|
ULandscapeComponent* EdgeComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX - 1, ComponentIndexY));
|
|
if (EdgeComponent && !EdgeComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (SubIndexX == ComponentNumSubsections - 1 && SubX == SubsectionSizeQuads)
|
|
{
|
|
ULandscapeComponent* EdgeComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX + 1, ComponentIndexY));
|
|
if (EdgeComponent && !EdgeComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// up / down
|
|
if (SubIndexY == 0 && SubY == 0)
|
|
{
|
|
ULandscapeComponent* EdgeComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY - 1));
|
|
if (EdgeComponent && !EdgeComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (SubIndexY == ComponentNumSubsections - 1 && SubY == SubsectionSizeQuads)
|
|
{
|
|
ULandscapeComponent* EdgeComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY + 1));
|
|
if (EdgeComponent && !EdgeComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// diagonals
|
|
if (SubIndexY == 0 && SubY == 0 && SubIndexX == 0 && SubX == 0)
|
|
{
|
|
ULandscapeComponent* CornerComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX - 1, ComponentIndexY - 1));
|
|
if (CornerComponent && !CornerComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (SubIndexY == 0 && SubY == 0 && SubIndexX == ComponentNumSubsections - 1 && SubX == SubsectionSizeQuads)
|
|
{
|
|
ULandscapeComponent* CornerComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX + 1, ComponentIndexY - 1));
|
|
if (CornerComponent && !CornerComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (SubIndexY == ComponentNumSubsections - 1 && SubY == SubsectionSizeQuads && SubIndexX == 0 && SubX == 0)
|
|
{
|
|
ULandscapeComponent* CornerComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX - 1, ComponentIndexY + 1));
|
|
if (CornerComponent && !CornerComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (SubIndexY == ComponentNumSubsections - 1 && SubY == SubsectionSizeQuads && SubIndexX == ComponentNumSubsections - 1 && SubX == SubsectionSizeQuads)
|
|
{
|
|
ULandscapeComponent* CornerComponent = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX + 1, ComponentIndexY + 1));
|
|
if (CornerComponent && !CornerComponent->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline TMap<const ULandscapeLayerInfoObject*, uint32> FLandscapeEditDataInterface::CountWeightBlendedLayerInfluence(const int32 ComponentIndexX, const int32 ComponentIndexY, TOptional<TArrayView<const uint8* const>> InOptionalLayerDataPtrs)
|
|
{
|
|
// the counts should easily fit in a uint32, a 255x255 x2x2 Component with weights of all 255 only totals 26 bits
|
|
checkSlow(FMath::CeilLogTwo(ComponentSizeQuads + 1) * 2 + 8 /*ceillog2(255)*/ <= 32);
|
|
TMap<const ULandscapeLayerInfoObject*, uint32> LayerInfluenceMap;
|
|
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindChecked(FIntPoint(ComponentIndexX,ComponentIndexY));
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
|
|
// used if InOptionalLayerDataPtrs is null
|
|
TArray<FLandscapeTextureDataInfo*, TInlineAllocator<2>> InternalTexDataInfos;
|
|
TArray<const uint8*, TInlineAllocator<8>> InternalLayerDataPtrs;
|
|
TArrayView<const uint8* const> LayerDataPtrs;
|
|
if (InOptionalLayerDataPtrs)
|
|
{
|
|
check(InOptionalLayerDataPtrs->Num() == ComponentWeightmapLayerAllocations.Num());
|
|
LayerDataPtrs = InOptionalLayerDataPtrs.GetValue();
|
|
}
|
|
else
|
|
{
|
|
InternalTexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num());
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx)
|
|
{
|
|
InternalTexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]);
|
|
}
|
|
|
|
InternalLayerDataPtrs.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
InternalLayerDataPtrs[LayerIdx] = (uint8*)InternalTexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel];
|
|
}
|
|
|
|
LayerDataPtrs = InternalLayerDataPtrs;
|
|
}
|
|
|
|
const int32 ScanlineSize = (SubsectionSizeQuads + 1) * ComponentNumSubsections * 4;
|
|
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
if (Allocation.LayerInfo == nullptr || Allocation.LayerInfo->bNoWeightBlend)
|
|
{
|
|
continue;
|
|
}
|
|
auto& Count = LayerInfluenceMap.Add(Allocation.LayerInfo, 0);
|
|
|
|
for (int32 SubIndexY = 0; SubIndexY < ComponentNumSubsections; ++SubIndexY)
|
|
{
|
|
const int32 YStart = SubIndexY * (SubsectionSizeQuads + 1);
|
|
const int32 YEnd = YStart + (SubsectionSizeQuads + 1);
|
|
for (int32 Y = YStart; Y < YEnd; ++Y)
|
|
{
|
|
for (int32 SubIndexX = 0; SubIndexX < ComponentNumSubsections; ++SubIndexX)
|
|
{
|
|
const int32 XStart = SubIndexX * (SubsectionSizeQuads + 1);
|
|
const int32 XEnd = XStart + (SubsectionSizeQuads + 1);
|
|
for (int32 X = XStart; X < XEnd; ++X)
|
|
{
|
|
const int32 TexDataIndex = Y * ScanlineSize + X * 4;
|
|
const uint8 Weight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
Count += Weight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LayerInfluenceMap.ValueSort(TGreater<uint32>());
|
|
return LayerInfluenceMap;
|
|
}
|
|
|
|
const ULandscapeLayerInfoObject* FLandscapeEditDataInterface::ChooseReplacementLayer(const ULandscapeLayerInfoObject* const LayerInfo, const int32 ComponentIndexX, const int32 SubIndexX, const int32 SubX, const int32 ComponentIndexY, const int32 SubIndexY, const int32 SubY, TMap<FIntPoint, TMap<const ULandscapeLayerInfoObject*, uint32>>& LayerInfluenceCache, TArrayView<const uint8* const> LayerDataPtrs)
|
|
{
|
|
const TMap<const ULandscapeLayerInfoObject*, uint32>* LayerInfluenceMapCacheEntry = LayerInfluenceCache.Find(FIntPoint(ComponentIndexX, ComponentIndexY));
|
|
if (!LayerInfluenceMapCacheEntry)
|
|
{
|
|
LayerInfluenceMapCacheEntry = &LayerInfluenceCache.Add(FIntPoint(ComponentIndexX, ComponentIndexY), CountWeightBlendedLayerInfluence(ComponentIndexX, ComponentIndexY, LayerDataPtrs));
|
|
}
|
|
|
|
if (!(SubIndexX == 0 && SubX == 0) &&
|
|
!(SubIndexX == ComponentNumSubsections - 1 && SubX == SubsectionSizeQuads) &&
|
|
!(SubIndexY == 0 && SubY == 0) &&
|
|
!(SubIndexY == ComponentNumSubsections - 1 && SubY == SubsectionSizeQuads))
|
|
{
|
|
for (const auto& LayerInfluenceMapPair : *LayerInfluenceMapCacheEntry)
|
|
{
|
|
if (LayerInfluenceMapPair.Key != LayerInfo)
|
|
{
|
|
return LayerInfluenceMapPair.Key;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TMap<const ULandscapeLayerInfoObject*, uint32, TInlineSetAllocator<8>> LayerInfluenceMap = *LayerInfluenceMapCacheEntry;
|
|
|
|
const int32 ComponentXStart = (SubIndexX == 0 && SubX == 0) ? ComponentIndexX - 1 : ComponentIndexX;
|
|
const int32 ComponentXEnd = (SubIndexX == ComponentNumSubsections - 1 && SubX == SubsectionSizeQuads) ? ComponentIndexX + 1 : ComponentIndexX;
|
|
const int32 ComponentYStart = (SubIndexY == 0 && SubY == 0) ? ComponentIndexY - 1 : ComponentIndexY;
|
|
const int32 ComponentYEnd = (SubIndexY == ComponentNumSubsections - 1 && SubY == SubsectionSizeQuads) ? ComponentIndexY + 1 : ComponentIndexY;
|
|
for (int32 Y = ComponentYStart; Y <= ComponentYEnd; ++Y)
|
|
{
|
|
for (int32 X = ComponentXStart; X <= ComponentXEnd; ++X)
|
|
{
|
|
if (X == ComponentIndexX && Y == ComponentIndexY)
|
|
{
|
|
// skip the current component, it is already included above
|
|
continue;
|
|
}
|
|
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, Y));
|
|
if (!Component)
|
|
{
|
|
// skip missing components
|
|
continue;
|
|
}
|
|
|
|
const TMap<const ULandscapeLayerInfoObject*, uint32>* OtherLayerInfluenceMapCacheEntry = LayerInfluenceCache.Find(FIntPoint(X, Y));
|
|
if (!OtherLayerInfluenceMapCacheEntry)
|
|
{
|
|
OtherLayerInfluenceMapCacheEntry = &LayerInfluenceCache.Add(FIntPoint(X, Y), CountWeightBlendedLayerInfluence(X, Y, {}));
|
|
}
|
|
|
|
for (auto LayerInfluenceMapIt = LayerInfluenceMap.CreateIterator(); LayerInfluenceMapIt; ++LayerInfluenceMapIt)
|
|
{
|
|
const uint32* Value = OtherLayerInfluenceMapCacheEntry->Find(LayerInfluenceMapIt->Key);
|
|
if (Value)
|
|
{
|
|
LayerInfluenceMapIt->Value += *Value;
|
|
}
|
|
else
|
|
{
|
|
// only allow layers that exist in *all* the touched components
|
|
LayerInfluenceMapIt.RemoveCurrent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LayerInfluenceMap.ValueSort(TGreater<uint32>());
|
|
for (const auto& LayerInfluenceMapPair : LayerInfluenceMap)
|
|
{
|
|
if (LayerInfluenceMapPair.Key != LayerInfo)
|
|
{
|
|
return LayerInfluenceMapPair.Key;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction PaintingRestriction /*= None*/, bool bWeightAdjust /*= true*/, bool bTotalWeightAdjust /*= false*/)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(LandscapeEditDataInterface_SetAlphaData);
|
|
|
|
check(LayerInfo != nullptr);
|
|
check(Data != nullptr);
|
|
if (LayerInfo->bNoWeightBlend)
|
|
{
|
|
bWeightAdjust = false;
|
|
}
|
|
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
|
|
check(ComponentSizeQuads > 0);
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components
|
|
int32 ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1;
|
|
int32 ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1;
|
|
int32 ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1;
|
|
|
|
TArray<FLandscapeTextureDataInfo*, TInlineAllocator<2>> TexDataInfos;
|
|
TArray<uint8*, TInlineAllocator<8>> LayerDataPtrs; // Pointers to all layers' data
|
|
TArray<bool, TInlineAllocator<8>> LayerNoWeightBlends; // NoWeightBlend flags
|
|
TArray<bool, TInlineAllocator<8>> LayerEditDataAllZero; // Whether the data we are editing for this layer is all zero
|
|
TArray<FColor*> CollisionWeightmapMipData;
|
|
TArray<FColor*> SimpleCollisionWeightmapMipData;
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
|
|
TMap<FIntPoint, TMap<const ULandscapeLayerInfoObject*, uint32>> LayerInfluenceCache;
|
|
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY);
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey);
|
|
|
|
// if NULL, there is no component at this location
|
|
if (Component == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (LayerInfo == ALandscape::VisibilityLayer && !Component->IsLandscapeHoleMaterialValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseComponentAllowList && !Component->LayerAllowList.Contains(LayerInfo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Component->Modify(GetShouldDirtyPackage());
|
|
|
|
TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
int32 UpdateLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; });
|
|
|
|
// Need allocation for weightmap
|
|
if (UpdateLayerIdx == INDEX_NONE)
|
|
{
|
|
const int32 LayerLimit = Component->GetLandscapeProxy()->MaxPaintedLayersPerComponent;
|
|
|
|
// if we can't allocate a layer, then there is nothing to paint
|
|
if (PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly ||
|
|
(PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && LayerLimit > 0 && ComponentWeightmapLayerAllocations.Num() >= LayerLimit))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto IsDataAllZeroForComponent = [&]() -> bool
|
|
{
|
|
// Find coordinates of box that lies inside component
|
|
const int32 ComponentX1 = FMath::Clamp<int32>(X1 - ComponentIndexX * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY1 = FMath::Clamp<int32>(Y1 - ComponentIndexY * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentX2 = FMath::Clamp<int32>(X2 - ComponentIndexX * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY2 = FMath::Clamp<int32>(Y2 - ComponentIndexY * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
const int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); // -1 because we need to pick up vertices shared between subsections
|
|
const int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
const int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
const int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
const int32 SubX1 = FMath::Clamp<int32>(ComponentX1 - SubsectionSizeQuads * SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY1 = FMath::Clamp<int32>(ComponentY1 - SubsectionSizeQuads * SubIndexY, 0, SubsectionSizeQuads);
|
|
const int32 SubX2 = FMath::Clamp<int32>(ComponentX2 - SubsectionSizeQuads * SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY2 = FMath::Clamp<int32>(ComponentY2 - SubsectionSizeQuads * SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
const int32 LandscapeX = SubIndexX * SubsectionSizeQuads + ComponentIndexX * ComponentSizeQuads + SubX;
|
|
const int32 LandscapeY = SubIndexY * SubsectionSizeQuads + ComponentIndexY * ComponentSizeQuads + SubY;
|
|
checkSlow(LandscapeX >= X1 && LandscapeX <= X2);
|
|
checkSlow(LandscapeY >= Y1 && LandscapeY <= Y2);
|
|
|
|
// Find the input data corresponding to this vertex
|
|
const int32 DataIndex = (LandscapeX - X1) + Stride * (LandscapeY - Y1);
|
|
uint8 NewWeight = Data[DataIndex];
|
|
if (NewWeight)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Avoid reallocating and update material instances if we're about to write all zeros and we don't have an allocated channel for this LayerInfo
|
|
if (IsDataAllZeroForComponent())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UpdateLayerIdx = ComponentWeightmapLayerAllocations.Num();
|
|
new (ComponentWeightmapLayerAllocations) FWeightmapLayerAllocationInfo(LayerInfo);
|
|
Component->ReallocateWeightmaps(/*DataInterface =*/this, GetEditLayer(), /*bInSaveToTransactionBuffer = */true, /*bool bInForceReallocate = */false, /*InTargetProxy = */nullptr, /*InRestrictSharingToComponents = */nullptr);
|
|
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
}
|
|
|
|
check(UpdateLayerIdx < ComponentWeightmapLayerAllocations.Num());
|
|
|
|
// Lock data for all the weightmaps
|
|
TexDataInfos.Reset();
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
TexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num());
|
|
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx)
|
|
{
|
|
TexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]);
|
|
}
|
|
|
|
LayerDataPtrs.Reset(); // Pointers to all layers' data
|
|
LayerDataPtrs.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
LayerNoWeightBlends.Reset(); // NoWeightBlend flags
|
|
LayerNoWeightBlends.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero
|
|
LayerEditDataAllZero.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
|
|
if (Allocation.LayerInfo != nullptr) // only take into account valid layer
|
|
{
|
|
LayerDataPtrs[LayerIdx] = (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel];
|
|
LayerNoWeightBlends[LayerIdx] = Allocation.LayerInfo->bNoWeightBlend;
|
|
LayerEditDataAllZero[LayerIdx] = true;
|
|
}
|
|
else
|
|
{
|
|
LayerDataPtrs[LayerIdx] = nullptr;
|
|
LayerNoWeightBlends[LayerIdx] = true;
|
|
LayerEditDataAllZero[LayerIdx] = false;
|
|
}
|
|
}
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
const int32 TexSize = (SubsectionSizeQuads+1) * ComponentNumSubsections;
|
|
|
|
// Find coordinates of box that lies inside component
|
|
const int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
const int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads, 0, ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
const int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads, 0, ComponentNumSubsections-1);
|
|
const int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections-1);
|
|
const int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections-1);
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
const int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
const int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
const int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
const int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
checkSlow( LandscapeX >= X1 && LandscapeX <= X2 );
|
|
checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 );
|
|
|
|
// Find the input data corresponding to this vertex
|
|
const int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1);
|
|
uint8 NewWeight = Data[DataIndex];
|
|
|
|
const int32 TexX = (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
const int32 TexY = (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
const int32 TexDataIndex = 4 * (TexX + TexY * TexSize);
|
|
|
|
uint8 CurrentWeight = LayerDataPtrs[UpdateLayerIdx][TexDataIndex];
|
|
if (NewWeight == CurrentWeight)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseComponentAllowList && NewWeight != 0)
|
|
{
|
|
bool bIsAllowed = IsLayerAllowed(LayerInfo, ComponentIndexX, SubIndexX, SubX, ComponentIndexY, SubIndexY, SubY);
|
|
if (!bIsAllowed)
|
|
{
|
|
NewWeight = 0;
|
|
}
|
|
}
|
|
|
|
// Adjust all layer weights
|
|
// (bWeightAdjust implies that this is a weight-blended layer)
|
|
int32 OtherLayerWeightSum = 0;
|
|
if (bWeightAdjust)
|
|
{
|
|
// Normalize all layers including the painted one
|
|
// gmartin: this isn't used. TODO: Remove
|
|
if (bTotalWeightAdjust)
|
|
{
|
|
int32 MaxLayerIdx = -1;
|
|
int32 MaxWeight = INT_MIN;
|
|
|
|
// Adjust other layers' weights accordingly
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerDataPtrs[LayerIdx] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
|
|
if (LayerIdx == UpdateLayerIdx)
|
|
{
|
|
ExistingWeight = NewWeight;
|
|
}
|
|
// Exclude bNoWeightBlend layers
|
|
if (LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
OtherLayerWeightSum += ExistingWeight;
|
|
if (MaxWeight < ExistingWeight)
|
|
{
|
|
MaxWeight = ExistingWeight;
|
|
MaxLayerIdx = LayerIdx;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OtherLayerWeightSum > 0 && OtherLayerWeightSum != 255)
|
|
{
|
|
const float Factor = 255.0f / OtherLayerWeightSum;
|
|
OtherLayerWeightSum = 0;
|
|
|
|
// Normalize
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerDataPtrs[LayerIdx] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
|
|
if (LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
// normalization...
|
|
ExistingWeight = (uint8)(Factor * ExistingWeight);
|
|
OtherLayerWeightSum += ExistingWeight;
|
|
|
|
if (ExistingWeight != 0)
|
|
{
|
|
LayerEditDataAllZero[LayerIdx] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((255 - OtherLayerWeightSum) && MaxLayerIdx >= 0)
|
|
{
|
|
// No need to check for nullptr here because MaxLayerIdx can only be set to a valid layer
|
|
LayerDataPtrs[MaxLayerIdx][TexDataIndex] += static_cast<uint8>(255 - OtherLayerWeightSum);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Adjust other layers' weights accordingly
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
// Exclude bNoWeightBlend layers
|
|
if (LayerIdx != UpdateLayerIdx && LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
// No need to check for nullptr here because invalid layers have LayerNoWeightBlends set to true.
|
|
OtherLayerWeightSum += LayerDataPtrs.IsValidIndex(LayerIdx) ? LayerDataPtrs[LayerIdx][TexDataIndex] : 0;
|
|
}
|
|
}
|
|
|
|
if (OtherLayerWeightSum == 0 && NewWeight < 255)
|
|
{
|
|
if (NewWeight < CurrentWeight)
|
|
{
|
|
// When reducing the layer weight from 255, we need to choose another layer to fill to avoid a black hole
|
|
const ULandscapeLayerInfoObject* ReplacementLayer = ChooseReplacementLayer(LayerInfo, ComponentIndexX, SubIndexX, SubX, ComponentIndexY, SubIndexY, SubY, LayerInfluenceCache, LayerDataPtrs);
|
|
if (ReplacementLayer)
|
|
{
|
|
const int32 ReplacementLayerIndex = ComponentWeightmapLayerAllocations.IndexOfByPredicate([&](const FWeightmapLayerAllocationInfo& AllocationInfo) { return AllocationInfo.LayerInfo == ReplacementLayer; });
|
|
|
|
// No need to check for nullptr here because ChooseReplacementLayer can't return an invalid layer.
|
|
LayerDataPtrs[ReplacementLayerIndex][TexDataIndex] = 255 - NewWeight;
|
|
LayerEditDataAllZero[ReplacementLayerIndex] = false;
|
|
}
|
|
else
|
|
{
|
|
// if we didn't find a suitable replacement we just have to leave it at 255, unfortunately
|
|
NewWeight = 255;
|
|
}
|
|
}
|
|
else if (NewWeight > CurrentWeight)
|
|
{
|
|
// if weight is increasing on a black spot then go straight to 255
|
|
NewWeight = 255;
|
|
}
|
|
|
|
// No need to check for nullptr here because UpdateLayerIdx is always valid
|
|
LayerDataPtrs[UpdateLayerIdx][TexDataIndex] = NewWeight;
|
|
}
|
|
else
|
|
{
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerDataPtrs[LayerIdx] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex];
|
|
|
|
if (LayerIdx == UpdateLayerIdx)
|
|
{
|
|
Weight = NewWeight;
|
|
}
|
|
else
|
|
{
|
|
// Exclude bNoWeightBlend layers
|
|
if (LayerNoWeightBlends[LayerIdx] == false)
|
|
{
|
|
Weight = static_cast<uint8>(FMath::Clamp(
|
|
FMath::RoundToInt((float)(255 - NewWeight) * (float)Weight / (float)OtherLayerWeightSum), 0, 255));
|
|
}
|
|
}
|
|
|
|
if (Weight != 0)
|
|
{
|
|
LayerEditDataAllZero[LayerIdx] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No need to check for nullptr here because UpdateLayerIdx is always valid
|
|
// Weight value set without adjusting other layers' weights
|
|
uint8& Weight = LayerDataPtrs[UpdateLayerIdx][TexDataIndex];
|
|
Weight = NewWeight;
|
|
if (Weight != 0)
|
|
{
|
|
LayerEditDataAllZero[UpdateLayerIdx] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
const int32 TexX1 = (SubsectionSizeQuads+1) * SubIndexX + SubX1;
|
|
const int32 TexY1 = (SubsectionSizeQuads+1) * SubIndexY + SubY1;
|
|
const int32 TexX2 = (SubsectionSizeQuads+1) * SubIndexX + SubX2;
|
|
const int32 TexY2 = (SubsectionSizeQuads+1) * SubIndexY + SubY2;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
if (TexDataInfos[WeightmapIdx] != NULL)
|
|
{
|
|
TexDataInfos[WeightmapIdx]->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
// Update mipmaps
|
|
CollisionWeightmapMipData.Reset();
|
|
CollisionWeightmapMipData.AddUninitialized(ComponentWeightmapTextures.Num());
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
UTexture2D* const WeightmapTexture = ComponentWeightmapTextures[WeightmapIdx];
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
WeightmapTextureMipData.Reset();
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
FColor* const MipData = (FColor*)TexDataInfos[WeightmapIdx]->GetMipData(MipIdx);
|
|
WeightmapTextureMipData[MipIdx] = MipData;
|
|
}
|
|
CollisionWeightmapMipData[WeightmapIdx] = WeightmapTextureMipData[Component->CollisionMipLevel];
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfos[WeightmapIdx]);
|
|
WeightmapTextureMipData.Reset();
|
|
}
|
|
|
|
if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel)
|
|
{
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
SimpleCollisionWeightmapMipData.Add((FColor*)TexDataInfos[WeightmapIdx]->GetMipData(Component->SimpleCollisionMipLevel));
|
|
}
|
|
}
|
|
|
|
// Update dominant layer info stored in collision component
|
|
Component->UpdateCollisionLayerData(
|
|
CollisionWeightmapMipData.GetData(),
|
|
Component->SimpleCollisionMipLevel > Component->CollisionMipLevel ? SimpleCollisionWeightmapMipData.GetData() : nullptr,
|
|
ComponentX1, ComponentY1, ComponentX2, ComponentY2);
|
|
CollisionWeightmapMipData.Reset();
|
|
SimpleCollisionWeightmapMipData.Reset();
|
|
}
|
|
|
|
// Check if we need to remove weightmap allocations for layers that were completely painted away
|
|
bool bRemovedLayer = false;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerEditDataAllZero[LayerIdx])
|
|
{
|
|
bool bLayerDeleted = Component->DeleteLayerIfAllZero(GetEditLayer(), LayerDataPtrs[LayerIdx], TexSize, LayerIdx, GetShouldDirtyPackage());
|
|
|
|
if (bLayerDeleted)
|
|
{
|
|
LayerEditDataAllZero.RemoveAt(LayerIdx);
|
|
LayerDataPtrs.RemoveAt(LayerIdx);
|
|
LayerIdx--;
|
|
|
|
bRemovedLayer = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bRemovedLayer)
|
|
{
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
}
|
|
|
|
Component->GetLandscapeProxy()->ValidateProxyLayersWeightmapUsage();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetAlphaData(const TSet<ULandscapeLayerInfoObject*>& DirtyLayerInfos, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction PaintingRestriction /*= None*/)
|
|
{
|
|
if (DirtyLayerInfos.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (ULandscapeLayerInfoObject* LayerInfo : DirtyLayerInfos)
|
|
{
|
|
// The Data[] array passed in is indexed by LandscapeInfo->GetLayerInfoIndex(),
|
|
// so if we're trying to write a layer which isn't in the LandscapeInfo,
|
|
// its data is either missing or written where another layer's data should be.
|
|
// Either way it's *very bad*.
|
|
check(LandscapeInfo->GetLayerInfoIndex(LayerInfo) != INDEX_NONE);
|
|
}
|
|
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1+X2-X1) * LandscapeInfo->Layers.Num();
|
|
}
|
|
|
|
check(ComponentSizeQuads > 0);
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components
|
|
int32 ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1;
|
|
int32 ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1;
|
|
int32 ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1;
|
|
|
|
TArray<ULandscapeLayerInfoObject*, TInlineAllocator<8>> NeedAllocationInfos;
|
|
TArray<FLandscapeTextureDataInfo*, TInlineAllocator<2>> TexDataInfos;
|
|
|
|
struct FLayerDataInfo
|
|
{
|
|
const uint8* InDataPtr;
|
|
uint8* TexDataPtr;
|
|
};
|
|
|
|
TArray<FLayerDataInfo, TInlineAllocator<8>> LayerDataInfos; // Pointers to all layers' data
|
|
TArray<bool, TInlineAllocator<8>> LayerEditDataAllZero; // Whether the data we are editing for this layer is all zero
|
|
TArray<FColor*> CollisionWeightmapMipData;
|
|
TArray<FColor*> SimpleCollisionWeightmapMipData;
|
|
TArray<FColor*> WeightmapTextureMipData;
|
|
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY);
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey);
|
|
|
|
// if NULL, there is no component at this location
|
|
if (Component == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int32 LayerLimit = Component->GetLandscapeProxy()->MaxPaintedLayersPerComponent;
|
|
|
|
NeedAllocationInfos.Reset();
|
|
|
|
TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
|
|
for (ULandscapeLayerInfoObject* LayerInfo : DirtyLayerInfos)
|
|
{
|
|
const bool bFound = ComponentWeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; });
|
|
if (!bFound)
|
|
{
|
|
NeedAllocationInfos.Add(LayerInfo);
|
|
}
|
|
}
|
|
|
|
// Need allocation for weightmaps
|
|
if (NeedAllocationInfos.Num() > 0)
|
|
{
|
|
if (NeedAllocationInfos.Num() == DirtyLayerInfos.Num())
|
|
{
|
|
if (PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly ||
|
|
(PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers &&
|
|
ComponentWeightmapLayerAllocations.Num() >= LayerLimit))
|
|
{
|
|
// nothing to paint to this component due to layer limit
|
|
continue;
|
|
}
|
|
}
|
|
if (PaintingRestriction != ELandscapeLayerPaintingRestriction::ExistingOnly)
|
|
{
|
|
Component->Modify(GetShouldDirtyPackage());
|
|
for (ULandscapeLayerInfoObject* LayerInfoNeedingAllocation : NeedAllocationInfos)
|
|
{
|
|
if (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers &&
|
|
LayerLimit > 0 && ComponentWeightmapLayerAllocations.Num() >= LayerLimit)
|
|
{
|
|
break;
|
|
}
|
|
ComponentWeightmapLayerAllocations.Emplace(LayerInfoNeedingAllocation);
|
|
}
|
|
Component->ReallocateWeightmaps(/*DataInterface =*/this, GetEditLayer(), /*bInSaveToTransactionBuffer = */true, /*bool bInForceReallocate = */false, /*InTargetProxy = */nullptr, /*InRestrictSharingToComponents = */nullptr);
|
|
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
}
|
|
}
|
|
|
|
// Lock data for all the weightmaps
|
|
TexDataInfos.Reset();
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
TexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num());
|
|
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx)
|
|
{
|
|
TexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]);
|
|
}
|
|
|
|
LayerDataInfos.Reset(); // Pointers to all layers' data
|
|
LayerDataInfos.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero
|
|
LayerEditDataAllZero.AddUninitialized(ComponentWeightmapLayerAllocations.Num());
|
|
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx];
|
|
const int32 LayerDataIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo);
|
|
check(LayerDataIdx != INDEX_NONE);
|
|
LayerDataInfos[LayerIdx].InDataPtr = Data + LayerDataIdx;
|
|
LayerDataInfos[LayerIdx].TexDataPtr = (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel];
|
|
LayerEditDataAllZero[LayerIdx] = true;
|
|
}
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
const int32 TexSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections;
|
|
|
|
// Find coordinates of box that lies inside component
|
|
const int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
const int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
const int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
const int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
const int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
const int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
const int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
const int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
const int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
const int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
const int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
checkSlow( LandscapeX >= X1 && LandscapeX <= X2 );
|
|
checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 );
|
|
|
|
// Find the input data corresponding to this vertex
|
|
const int32 DataIndex = (LandscapeY-Y1) * Stride + (LandscapeX-X1) * LandscapeInfo->Layers.Num();
|
|
|
|
// Adjust all layer weights
|
|
const int32 TexX = (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
const int32 TexY = (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
|
|
const int32 TexDataIndex = 4 * (TexX + TexY * TexSize);
|
|
|
|
int32 OtherLayerWeightSum = 0;
|
|
|
|
// Apply weights to all layers simultaneously.
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
// this is equivalent to saying if (DirtyLayerInfos.Contains(Allocation.LayerInfo))
|
|
// which is what we really mean here, but this is quicker
|
|
// and I've lost count of the depth we've nested for loops at this point
|
|
if (LayerDataInfos[LayerIdx].TexDataPtr != NULL)
|
|
{
|
|
uint8& Weight = LayerDataInfos[LayerIdx].TexDataPtr[TexDataIndex];
|
|
|
|
Weight = LayerDataInfos[LayerIdx].InDataPtr[DataIndex]; // Only for whole weight
|
|
if (Weight != 0)
|
|
{
|
|
LayerEditDataAllZero[LayerIdx] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
const int32 TexX1 = (SubsectionSizeQuads+1) * SubIndexX + SubX1;
|
|
const int32 TexY1 = (SubsectionSizeQuads+1) * SubIndexY + SubY1;
|
|
const int32 TexX2 = (SubsectionSizeQuads+1) * SubIndexX + SubX2;
|
|
const int32 TexY2 = (SubsectionSizeQuads+1) * SubIndexY + SubY2;
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
if (TexDataInfos[WeightmapIdx] != NULL)
|
|
{
|
|
TexDataInfos[WeightmapIdx]->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
// Update mipmaps
|
|
CollisionWeightmapMipData.Reset();
|
|
CollisionWeightmapMipData.AddUninitialized(ComponentWeightmapTextures.Num());
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
UTexture2D* const WeightmapTexture = ComponentWeightmapTextures[WeightmapIdx];
|
|
|
|
const int32 NumMips = WeightmapTexture->Source.GetNumMips();
|
|
WeightmapTextureMipData.Reset();
|
|
WeightmapTextureMipData.AddUninitialized(NumMips);
|
|
for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++)
|
|
{
|
|
FColor* const MipData = (FColor*)TexDataInfos[WeightmapIdx]->GetMipData(MipIdx);
|
|
WeightmapTextureMipData[MipIdx] = MipData;
|
|
}
|
|
CollisionWeightmapMipData[WeightmapIdx] = WeightmapTextureMipData[Component->CollisionMipLevel];
|
|
|
|
ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfos[WeightmapIdx]);
|
|
WeightmapTextureMipData.Reset();
|
|
}
|
|
|
|
if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel)
|
|
{
|
|
for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++)
|
|
{
|
|
SimpleCollisionWeightmapMipData.Add((FColor*)TexDataInfos[WeightmapIdx]->GetMipData(Component->SimpleCollisionMipLevel));
|
|
}
|
|
}
|
|
|
|
// Update dominant layer info stored in collision component
|
|
Component->UpdateCollisionLayerData(
|
|
CollisionWeightmapMipData.GetData(),
|
|
Component->SimpleCollisionMipLevel > Component->CollisionMipLevel ? SimpleCollisionWeightmapMipData.GetData() : nullptr,
|
|
ComponentX1, ComponentY1, ComponentX2, ComponentY2);
|
|
CollisionWeightmapMipData.Reset();
|
|
SimpleCollisionWeightmapMipData.Reset();
|
|
}
|
|
|
|
// Check if we need to remove weightmap allocations for layers that were completely painted away
|
|
bool bRemovedLayer = false;
|
|
for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++)
|
|
{
|
|
if (LayerEditDataAllZero[LayerIdx])
|
|
{
|
|
bool bLayerDeleted = Component->DeleteLayerIfAllZero(GetEditLayer(), LayerDataInfos[LayerIdx].TexDataPtr, TexSize, LayerIdx, GetShouldDirtyPackage());
|
|
|
|
if (bLayerDeleted)
|
|
{
|
|
LayerEditDataAllZero.RemoveAt(LayerIdx);
|
|
LayerDataInfos.RemoveAt(LayerIdx);
|
|
LayerIdx--;
|
|
|
|
bRemovedLayer = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bRemovedLayer)
|
|
{
|
|
if (!Component->GetLandscapeProxy()->HasLayersContent())
|
|
{
|
|
Component->UpdateMaterialInstances();
|
|
Component->EditToolRenderData.UpdateDebugColorMaterial(Component);
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
Component->RequestWeightmapUpdate();
|
|
}
|
|
|
|
Component->GetLandscapeProxy()->ValidateProxyLayersWeightmapUsage();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData)
|
|
{
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY));
|
|
|
|
if( !Component )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UTexture2D* WeightmapTexture = NULL;
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
uint8* WeightmapTextureData = NULL;
|
|
uint8 WeightmapChannelOffset = 0;
|
|
TArray<FLandscapeTextureDataInfo*> TexDataInfos; // added for whole weight case...
|
|
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
|
|
if (LayerInfo != NULL)
|
|
{
|
|
for( int32 LayerIdx=0;LayerIdx<ComponentWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
WeightmapTexture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
TexDataInfo = GetTextureDataInfo(WeightmapTexture);
|
|
WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0);
|
|
WeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Lock data for all the weightmaps
|
|
for( int32 WeightmapIdx=0;WeightmapIdx < ComponentWeightmapTextures.Num();WeightmapIdx++ )
|
|
{
|
|
TexDataInfos.Add(GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]));
|
|
}
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
if (LayerInfo != NULL)
|
|
{
|
|
// Find the input data corresponding to this vertex
|
|
uint8 Weight;
|
|
if( WeightmapTexture )
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = WeightmapTexture->Source.GetSizeX();
|
|
int32 SizeV = WeightmapTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
Weight = WeightmapTextureData[ 4 * (TexX + TexY * SizeU) + WeightmapChannelOffset ];
|
|
}
|
|
else
|
|
{
|
|
Weight = 0;
|
|
}
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, Weight);
|
|
}
|
|
else // Whole weight map case...
|
|
{
|
|
StoreData.PreInit(LandscapeInfo->Layers.Num());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<ComponentWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
int32 Idx = ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex;
|
|
UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[Idx];
|
|
uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0);
|
|
uint8 ComponentWeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX();
|
|
int32 SizeV = ComponentWeightmapTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
|
|
uint8 Weight = ComponentWeightmapTextureData[ 4 * (TexX + TexY * SizeU) + ComponentWeightmapChannelOffset ];
|
|
|
|
// Find index in LayerInfos
|
|
{
|
|
int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo);
|
|
if (LayerInfoIdx != INDEX_NONE)
|
|
{
|
|
StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8 FLandscapeEditDataInterface::GetWeightMapData(const ULandscapeComponent* Component, ULandscapeLayerInfoObject* LayerInfo, int32 TexU, int32 TexV, uint8 Offset /*= 0*/, UTexture2D* Texture /*= NULL*/, uint8* TextureData /*= NULL*/)
|
|
{
|
|
check(Component);
|
|
if (!Texture || !TextureData)
|
|
{
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<ComponentWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
Texture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Texture);
|
|
TextureData = (uint8*)TexDataInfo->GetMipData(0);
|
|
Offset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Texture && TextureData)
|
|
{
|
|
const int32 SizeU = Texture->Source.GetSizeX();
|
|
const int32 SizeV = Texture->Source.GetSizeY();
|
|
const int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
const int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
const int32 TexX = WeightmapOffsetX + TexU;
|
|
const int32 TexY = WeightmapOffsetY + TexV;
|
|
return TextureData[ 4 * (TexX + TexY * SizeU) + Offset ];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* LayerInfo, int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData)
|
|
{
|
|
// Copy variables
|
|
int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2;
|
|
ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN;
|
|
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
int32 ComponentSizeX = ComponentIndexX2-ComponentIndexX1+1;
|
|
int32 ComponentSizeY = ComponentIndexY2-ComponentIndexY1+1;
|
|
|
|
// Neighbor Components
|
|
ULandscapeComponent* BorderComponent[4] = {0, 0, 0, 0};
|
|
ULandscapeComponent* CornerComponent[4] = {0, 0, 0, 0};
|
|
bool NoBorderX1 = false, NoBorderX2 = false;
|
|
TArray<bool> NoBorderY1, NoBorderY2, ComponentDataExist;
|
|
TArray<ULandscapeComponent*> BorderComponentY1, BorderComponentY2;
|
|
ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY);
|
|
ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY);
|
|
bool bHasMissingValue = false;
|
|
|
|
UTexture2D* NeighborWeightmapTexture[4] = {0, 0, 0, 0};
|
|
FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = {0, 0, 0, 0};
|
|
uint8* NeighborWeightmapTextureData[4] = {0, 0, 0, 0};
|
|
uint8 NeighborWeightmapChannelOffset[4] = {0, 0, 0, 0};
|
|
uint8 CornerValues[4] = {0, 0, 0, 0};
|
|
int32 EdgeCoord = (SubsectionSizeQuads+1) * ComponentNumSubsections - 1; //ComponentSizeQuads;
|
|
|
|
// initial loop....
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
NoBorderX1 = false;
|
|
NoBorderX2 = false;
|
|
BorderComponent[0] = BorderComponent[1] = NULL;
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
BorderComponent[2] = BorderComponent[3] = NULL;
|
|
int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1;
|
|
int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1;
|
|
int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1;
|
|
ComponentDataExist[ComponentIndexXY] = false;
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY));
|
|
|
|
UTexture2D* WeightmapTexture = NULL;
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
uint8* WeightmapTextureData = NULL;
|
|
uint8 WeightmapChannelOffset = 0;
|
|
TArray<FLandscapeTextureDataInfo*> TexDataInfos; // added for whole weight case...
|
|
uint8 CornerSet = 0;
|
|
bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ];
|
|
bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ];
|
|
bool IsBorderPosition = false;
|
|
|
|
if( Component )
|
|
{
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<ComponentWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
WeightmapTexture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
TexDataInfo = GetTextureDataInfo(WeightmapTexture);
|
|
WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0);
|
|
WeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Lock data for all the weightmaps
|
|
for( int32 WeightmapIdx=0;WeightmapIdx < ComponentWeightmapTextures.Num();WeightmapIdx++ )
|
|
{
|
|
TexDataInfos.Add(GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]));
|
|
}
|
|
}
|
|
ComponentDataExist[ComponentIndexXY] = true;
|
|
// Update valid region
|
|
ValidX1 = FMath::Min<int32>(Component->GetSectionBase().X, ValidX1);
|
|
ValidX2 = FMath::Max<int32>(Component->GetSectionBase().X+ComponentSizeQuads, ValidX2);
|
|
ValidY1 = FMath::Min<int32>(Component->GetSectionBase().Y, ValidY1);
|
|
ValidY2 = FMath::Max<int32>(Component->GetSectionBase().Y+ComponentSizeQuads, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
if (!bHasMissingValue)
|
|
{
|
|
NoBorderY1.Empty(ComponentSizeX);
|
|
NoBorderY2.Empty(ComponentSizeX);
|
|
NoBorderY1.AddZeroed(ComponentSizeX);
|
|
NoBorderY2.AddZeroed(ComponentSizeX);
|
|
BorderComponentY1.Empty(ComponentSizeX);
|
|
BorderComponentY2.Empty(ComponentSizeX);
|
|
BorderComponentY1.AddZeroed(ComponentSizeX);
|
|
BorderComponentY2.AddZeroed(ComponentSizeX);
|
|
bHasMissingValue = true;
|
|
}
|
|
|
|
if (LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX - 1, ComponentIndexY)) == nullptr
|
|
|| LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY - 1)) == nullptr
|
|
|| LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX + 1, ComponentIndexY)) == nullptr
|
|
|| LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY + 1)) == nullptr)
|
|
{
|
|
IsBorderPosition = true;
|
|
}
|
|
|
|
// Search for neighbor component for interpolation
|
|
bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX);
|
|
bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY);
|
|
// Search for left-closest component
|
|
if ( bShouldSearchX || (!NoBorderX1 && !BorderComponent[0]))
|
|
{
|
|
NoBorderX1 = true;
|
|
for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--)
|
|
{
|
|
BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY));
|
|
if (BorderComponent[0])
|
|
{
|
|
NoBorderX1 = false;
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[0]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[0]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[0] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[0] = GetTextureDataInfo(NeighborWeightmapTexture[0]);
|
|
NeighborWeightmapTextureData[0] = (uint8*)NeighborTexDataInfo[0]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[0] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for right-closest component
|
|
if ( bShouldSearchX || (!NoBorderX2 && !BorderComponent[1]))
|
|
{
|
|
NoBorderX2 = true;
|
|
for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++)
|
|
{
|
|
BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY));
|
|
if (BorderComponent[1])
|
|
{
|
|
NoBorderX2 = false;
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[1]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[1]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[1] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[1] = GetTextureDataInfo(NeighborWeightmapTexture[1]);
|
|
NeighborWeightmapTextureData[1] = (uint8*)NeighborTexDataInfo[1]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[1] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Search for up-closest component
|
|
if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX]))
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--)
|
|
{
|
|
BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y));
|
|
if (BorderComponent[2])
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = false;
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[2]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[2]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[2] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]);
|
|
NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[2] = BorderComponentY1[ComponentIndexXX];
|
|
if (BorderComponent[2])
|
|
{
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[2]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[2]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[2] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]);
|
|
NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Search for bottom-closest component
|
|
if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX]))
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++)
|
|
{
|
|
BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y));
|
|
if (BorderComponent[3])
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = false;
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[3]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[3]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[3] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]);
|
|
NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[3] = BorderComponentY2[ComponentIndexXX];
|
|
if (BorderComponent[3])
|
|
{
|
|
if (LayerInfo != NULL)
|
|
{
|
|
const TArray<FWeightmapLayerAllocationInfo>& BorderWeightmapLayerAllocations = BorderComponent[3]->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& BorderWeightmapTextures = BorderComponent[3]->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<BorderWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo )
|
|
{
|
|
NeighborWeightmapTexture[3] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex];
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]);
|
|
NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY-1))) : NULL;
|
|
CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY-1))) : NULL;
|
|
CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY+1))) : NULL;
|
|
CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY+1))) : NULL;
|
|
|
|
if (CornerComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetWeightMapData(CornerComponent[0], LayerInfo, EdgeCoord, EdgeCoord);
|
|
}
|
|
else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads )
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, 0, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetWeightMapData(BorderComponent[2], LayerInfo, 0, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetWeightMapData(CornerComponent[1], LayerInfo, 0, EdgeCoord);
|
|
}
|
|
else if (ExistUp && X2 >= (ComponentIndexX+1)*ComponentSizeQuads)
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = StoreData.Load( (ComponentIndexX+1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, 0, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetWeightMapData(BorderComponent[2], LayerInfo, EdgeCoord, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[2])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetWeightMapData(CornerComponent[2], LayerInfo, EdgeCoord, 0);
|
|
}
|
|
else if (ExistLeft && Y2 >= (ComponentIndexY+1)*ComponentSizeQuads) // Use data already stored for 0, 2
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, (ComponentIndexY+1)*ComponentSizeQuads);
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, EdgeCoord, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetWeightMapData(BorderComponent[3], LayerInfo, 0, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]);
|
|
}
|
|
|
|
if (CornerComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetWeightMapData(CornerComponent[3], LayerInfo, 0, 0);
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, EdgeCoord, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetWeightMapData(BorderComponent[3], LayerInfo, EdgeCoord, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]);
|
|
}
|
|
|
|
FillCornerValues(CornerSet, CornerValues);
|
|
ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet;
|
|
}
|
|
|
|
if (!ComponentDataExist[ComponentIndexXY])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
if (LayerInfo != NULL)
|
|
{
|
|
// Find the input data corresponding to this vertex
|
|
uint8 Weight = 0;
|
|
if( WeightmapTexture )
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
Weight = GetWeightMapData(Component, LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, (SubsectionSizeQuads+1) * SubIndexY + SubY, WeightmapChannelOffset, WeightmapTexture, WeightmapTextureData );
|
|
}
|
|
// only try generating between component if we have no component, otherwise we might end up trying to add some layer info to some component that do not originally had the allocation.
|
|
else if (Component == nullptr && !IsBorderPosition)
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
uint8 Value[4] = {0, 0, 0, 0};
|
|
int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX};
|
|
float ValueX = 0.0f, ValueY = 0.0f;
|
|
bool Exist[4] = {false, false, false, false};
|
|
|
|
// Use data already stored for 0, 2
|
|
if (ExistLeft && SubX == 0)
|
|
{
|
|
Value[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, LandscapeY);
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else if (BorderComponent[0] && NeighborWeightmapTexture[0])
|
|
{
|
|
Value[0] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]);
|
|
Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 2))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[0] = static_cast<uint8>((float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2));
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[1] && NeighborWeightmapTexture[1])
|
|
{
|
|
Value[1] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]);
|
|
Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[1] = static_cast<uint8>((float)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
}
|
|
|
|
if (ExistUp && SubY == 0)
|
|
{
|
|
Value[2] = StoreData.Load( LandscapeX, ComponentIndexY*ComponentSizeQuads);
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else if (BorderComponent[2] && NeighborWeightmapTexture[2])
|
|
{
|
|
Value[2] = GetWeightMapData(BorderComponent[2], LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]);
|
|
Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 1))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[2] = static_cast<uint8>((float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2));
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[3] && NeighborWeightmapTexture[3])
|
|
{
|
|
Value[3] = GetWeightMapData(BorderComponent[3], LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]);
|
|
Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX;
|
|
Value[3] = static_cast<uint8>((float)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2));
|
|
Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
}
|
|
|
|
CalcInterpValue<uint8>(Dist, Exist, Value, ValueX, ValueY);
|
|
|
|
uint8 FinalValue = 0; // Default Value
|
|
if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = CalcValueFromValueXY<uint8>(Dist, ValueX, ValueY, CornerSet, CornerValues);
|
|
}
|
|
else if ( (Exist[0] || Exist[1]) )
|
|
{
|
|
FinalValue = static_cast<uint8>(ValueX);
|
|
}
|
|
else if ( (Exist[2] || Exist[3]) )
|
|
{
|
|
FinalValue = static_cast<uint8>(ValueY);
|
|
}
|
|
|
|
Weight = FinalValue;
|
|
}
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, Weight);
|
|
}
|
|
else // Whole weight map case... no interpolation now...
|
|
{
|
|
StoreData.PreInit(LandscapeInfo->Layers.Num());
|
|
|
|
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(GetEditLayer());
|
|
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(GetEditLayer());
|
|
|
|
for( int32 LayerIdx=0;LayerIdx<ComponentWeightmapLayerAllocations.Num();LayerIdx++ )
|
|
{
|
|
int32 Idx = ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex;
|
|
UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[Idx];
|
|
uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0);
|
|
uint8 ComponentWeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX();
|
|
int32 SizeV = ComponentWeightmapTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
|
|
uint8 Weight = ComponentWeightmapTextureData[ 4 * (TexX + TexY * SizeU) + ComponentWeightmapChannelOffset ];
|
|
|
|
// Find index in LayerInfos
|
|
{
|
|
int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo);
|
|
if (LayerInfoIdx != INDEX_NONE)
|
|
{
|
|
StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasMissingValue)
|
|
{
|
|
// generate something to fill in any missing data within the requested region
|
|
CalcMissingValues<uint8, TStoreData, float>( X1, X2, Y1, Y2,
|
|
ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2,
|
|
ComponentSizeX, ComponentSizeY, CornerValues,
|
|
NoBorderY1, NoBorderY2, ComponentDataExist, StoreData );
|
|
|
|
// clamp the returned bounds to the original request bounds
|
|
// this means the returned bounds are allowed to shrink (if there is only a smaller part of the request that actually exists)
|
|
// but it is not allowed to expand beyond the original request
|
|
ValidX1 = FMath::Max<int32>(X1, ValidX1);
|
|
ValidX2 = FMath::Min<int32>(X2, ValidX2);
|
|
ValidY1 = FMath::Max<int32>(Y1, ValidY1);
|
|
ValidY2 = FMath::Min<int32>(Y2, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
// if there were no missing values, then the entire requested bounds is returned
|
|
ValidX1 = X1;
|
|
ValidX2 = X2;
|
|
ValidY1 = Y1;
|
|
ValidY2 = Y2;
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetWeightDataTempl(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, uint8>& SparseData)
|
|
{
|
|
TSparseStoreData<uint8> SparseStoreData(SparseData);
|
|
GetWeightDataTempl(LayerInfo, X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData)
|
|
{
|
|
TSparseStoreData<uint8> SparseStoreData(SparseData);
|
|
GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TArray<uint8>* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<TArray<uint8>> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, TArray<uint8>>& SparseData)
|
|
{
|
|
TSparseStoreData<TArray<uint8>> SparseStoreData(SparseData);
|
|
GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
FLandscapeTextureDataInfo* FLandscapeTextureDataInterface::GetTextureDataInfo(UTexture2D* Texture)
|
|
{
|
|
FLandscapeTextureDataInfo* Result = TextureDataMap.FindRef(Texture);
|
|
if( !Result )
|
|
{
|
|
Result = TextureDataMap.Add(Texture, new FLandscapeTextureDataInfo(Texture, GetShouldDirtyPackage()));
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureChannel(UTexture2D* Dest, int32 DestChannel, UTexture2D* Src, int32 SrcChannel)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
FLandscapeTextureDataInfo* SrcDataInfo = GetTextureDataInfo(Src);
|
|
int32 MipSize = Dest->Source.GetSizeX();
|
|
check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY() && Src->Source.GetSizeX() == Dest->Source.GetSizeX());
|
|
|
|
int32 NumMips = DestDataInfo->NumMips();
|
|
int32 SrcMips = SrcDataInfo->NumMips();
|
|
if (SrcMips != NumMips)
|
|
{
|
|
// We could migrate data from an old landscape that still had multiple mips in its edit layer data so it's not worth displaying a warning then since no data loss will actually occur :
|
|
if (SrcMips < NumMips)
|
|
{
|
|
UE_LOG(LogLandscape, Warning, TEXT("Unexpected mip count mismatch when copying landscape texture channels from '%s' (%d mips) to '%s' (%d mips) -- mip data may be lost, or left uninitialized"),
|
|
*Src->GetPathName(), SrcMips, *Dest->GetPathName(), NumMips);
|
|
}
|
|
NumMips = FMath::Min(NumMips, SrcMips);
|
|
}
|
|
|
|
for( int32 MipIdx=0;MipIdx< NumMips;MipIdx++ )
|
|
{
|
|
uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx);
|
|
uint8* SrcTextureData = (uint8*)SrcDataInfo->GetMipData(MipIdx);
|
|
|
|
if (DestTextureData && SrcTextureData)
|
|
{
|
|
DestTextureData += ChannelOffsets[DestChannel];
|
|
SrcTextureData += ChannelOffsets[SrcChannel];
|
|
|
|
for (int32 i = 0; i < FMath::Square(MipSize); i++)
|
|
{
|
|
DestTextureData[i * 4] = SrcTextureData[i * 4];
|
|
}
|
|
}
|
|
|
|
DestDataInfo->AddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1);
|
|
MipSize >>= 1;
|
|
}
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureFromHeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, int32 SrcChannel)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
int32 MipSize = Dest->Source.GetSizeX();
|
|
check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY());
|
|
|
|
for( int32 MipIdx=0;MipIdx<DestDataInfo->NumMips();MipIdx++ )
|
|
{
|
|
FLandscapeComponentDataInterface DataInterface(Comp, MipIdx);
|
|
TArray<FColor> Heightmap;
|
|
DataInterface.GetHeightmapTextureData(Heightmap);
|
|
|
|
uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel];
|
|
uint8* SrcTextureData = (uint8*)Heightmap.GetData() + ChannelOffsets[SrcChannel];
|
|
|
|
for( int32 i=0;i<FMath::Square(MipSize);i++ )
|
|
{
|
|
DestTextureData[i*4] = SrcTextureData[i*4];
|
|
}
|
|
|
|
DestDataInfo->AddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1);
|
|
MipSize >>= 1;
|
|
}
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureFromHeightmap(UTexture2D* Dest, ULandscapeComponent* Comp, int32 MipIndex)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
const int32 MipSize = DestDataInfo->GetMipSizeX(MipIndex);
|
|
check(MipSize == DestDataInfo->GetMipSizeY(MipIndex));
|
|
|
|
const bool bEditingLayer = false;
|
|
FLandscapeComponentDataInterface DataInterface(Comp, MipIndex, bEditingLayer);
|
|
|
|
FColor* DestTextureData = (FColor*)DestDataInfo->GetMipData(MipIndex);
|
|
FColor* SrcTextureData = DataInterface.GetRawHeightData();
|
|
|
|
int32 MipSizeSquare = FMath::Square(MipSize);
|
|
check(MipSizeSquare == DataInterface.GetHeightmapSizeX(MipIndex)*DataInterface.GetHeightmapSizeY(MipIndex));
|
|
|
|
FMemory::Memcpy(DestTextureData, SrcTextureData, MipSizeSquare*sizeof(FColor));
|
|
DestDataInfo->AddMipUpdateRegion(MipIndex, 0, 0, MipSize - 1, MipSize - 1);
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureFromWeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
for (int32 MipIdx = 0; MipIdx < DestDataInfo->NumMips(); MipIdx++)
|
|
{
|
|
CopyTextureFromWeightmap(DestDataInfo, DestChannel, Comp, LayerInfo, MipIdx);
|
|
}
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureFromWeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo, int32 MipIndex)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
CopyTextureFromWeightmap(DestDataInfo, DestChannel, Comp, LayerInfo, MipIndex);
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::CopyTextureFromWeightmap(FLandscapeTextureDataInfo* DestDataInfo, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo, int32 MipIndex)
|
|
{
|
|
const int32 MipSize = DestDataInfo->GetMipSizeX(MipIndex);
|
|
check(MipSize == DestDataInfo->GetMipSizeY(MipIndex));
|
|
|
|
const bool bEditingLayer = false;
|
|
FLandscapeComponentDataInterface DataInterface(Comp, MipIndex, bEditingLayer);
|
|
TArray<uint8> WeightData;
|
|
DataInterface.GetWeightmapTextureData(LayerInfo, WeightData, bEditingLayer);
|
|
int32 MipSizeSquare = FMath::Square(MipSize);
|
|
check(WeightData.Num() == MipSizeSquare);
|
|
|
|
uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIndex) + ChannelOffsets[DestChannel];
|
|
for (int32 i = 0; i < MipSizeSquare; i++)
|
|
{
|
|
DestTextureData[i * 4] = WeightData[i];
|
|
}
|
|
|
|
DestDataInfo->AddMipUpdateRegion(MipIndex, 0, 0, MipSize - 1, MipSize - 1);
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::ZeroTextureChannel(UTexture2D* Dest, int32 DestChannel)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
int32 MipSize = Dest->Source.GetSizeX();
|
|
check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY());
|
|
|
|
for( int32 MipIdx=0;MipIdx<DestDataInfo->NumMips();MipIdx++ )
|
|
{
|
|
uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel];
|
|
|
|
for( int32 i=0;i<FMath::Square(MipSize);i++ )
|
|
{
|
|
DestTextureData[i*4] = 0;
|
|
}
|
|
|
|
DestDataInfo->AddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1);
|
|
MipSize >>= 1;
|
|
}
|
|
}
|
|
|
|
template<typename TData>
|
|
void FLandscapeTextureDataInterface::SetTextureValueTempl(UTexture2D* Dest, TData Value)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest);
|
|
int32 MipSize = Dest->Source.GetSizeX();
|
|
check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY());
|
|
|
|
for( int32 MipIdx=0;MipIdx<DestDataInfo->NumMips();MipIdx++ )
|
|
{
|
|
TData* DestTextureData = (TData*)DestDataInfo->GetMipData(MipIdx);
|
|
|
|
for( int32 i=0;i<FMath::Square(MipSize);i++ )
|
|
{
|
|
DestTextureData[i] = Value;
|
|
}
|
|
|
|
DestDataInfo->AddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1);
|
|
MipSize >>= 1;
|
|
}
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::ZeroTexture(UTexture2D* Dest)
|
|
{
|
|
SetTextureValueTempl<uint8>(Dest, 0);
|
|
}
|
|
|
|
void FLandscapeTextureDataInterface::SetTextureValue(UTexture2D* Dest, FColor Value)
|
|
{
|
|
SetTextureValueTempl<FColor>(Dest, Value);
|
|
}
|
|
|
|
template<typename TData>
|
|
bool FLandscapeTextureDataInterface::EqualTextureValueTempl(UTexture2D* Src, TData Value)
|
|
{
|
|
FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Src);
|
|
TData* DestTextureData = (TData*)DestDataInfo->GetMipData(0);
|
|
int32 Size = Src->Source.GetSizeX() * Src->Source.GetSizeY();
|
|
|
|
for( int32 i = 0 ;i < Size; ++i )
|
|
{
|
|
if (DestTextureData[i] != Value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FLandscapeTextureDataInterface::EqualTextureValue(UTexture2D* Src, FColor Value)
|
|
{
|
|
return EqualTextureValueTempl<FColor>(Src, Value);
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetSelectDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData)
|
|
{
|
|
auto ReturnComponentTexture = [&](ULandscapeComponent* Component) -> UTexture2D*
|
|
{
|
|
check(Component);
|
|
return Component->EditToolRenderData.DataTexture;
|
|
};
|
|
GetEditToolTextureData(X1, Y1, X2, Y2, StoreData, ReturnComponentTexture);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData)
|
|
{
|
|
TSparseStoreData<uint8> SparseStoreData(SparseData);
|
|
GetSelectDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetSelectDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetSelectData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride)
|
|
{
|
|
auto ReturnComponentTexture = [&](ULandscapeComponent* Component) -> UTexture2D*&
|
|
{
|
|
check(Component);
|
|
return static_cast<UTexture2D*&>(Component->EditToolRenderData.DataTexture);
|
|
};
|
|
SetEditToolTextureData(X1, Y1, X2, Y2, Data, Stride, ReturnComponentTexture, TEXTUREGROUP_8BitData);
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetLayerContributionDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData)
|
|
{
|
|
auto ReturnComponentTexture = [&](const ULandscapeComponent* Component) -> UTexture2D*
|
|
{
|
|
check(Component);
|
|
return Component->EditToolRenderData.LayerContributionTexture;
|
|
};
|
|
GetEditToolTextureData(X1, Y1, X2, Y2, StoreData, ReturnComponentTexture);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetLayerContributionData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData)
|
|
{
|
|
TSparseStoreData<uint8> SparseStoreData(SparseData);
|
|
GetLayerContributionDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetLayerContributionData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
TArrayStoreData<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetLayerContributionDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
|
|
void FLandscapeEditDataInterface::SetLayerContributionData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride)
|
|
{
|
|
auto ReturnComponentTexture = [](ULandscapeComponent* Component) -> UTexture2D*&
|
|
{
|
|
check(Component);
|
|
return static_cast<UTexture2D*&>(Component->EditToolRenderData.LayerContributionTexture);
|
|
};
|
|
SetEditToolTextureData(X1, Y1, X2, Y2, Data, Stride, ReturnComponentTexture, TEXTUREGROUP_8BitData);
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetDirtyDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData)
|
|
{
|
|
auto ReturnComponentTexture = [&](const ULandscapeComponent* Component) -> UTexture2D*
|
|
{
|
|
check(Component);
|
|
return Component->EditToolRenderData.DirtyTexture;
|
|
};
|
|
GetEditToolTextureData(X1, Y1, X2, Y2, StoreData, ReturnComponentTexture);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetDirtyData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, uint8>& SparseData)
|
|
{
|
|
TSparseStoreData<uint8> SparseStoreData(SparseData);
|
|
GetDirtyDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetDirtyData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
TArrayStoreData<uint8> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetDirtyDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
|
|
void FLandscapeEditDataInterface::SetDirtyData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride)
|
|
{
|
|
auto ReturnComponentTexture = [](ULandscapeComponent* Component) -> UTexture2D*&
|
|
{
|
|
check(Component);
|
|
return static_cast<UTexture2D*&>(Component->EditToolRenderData.DirtyTexture);
|
|
};
|
|
SetEditToolTextureData(X1, Y1, X2, Y2, Data, Stride, ReturnComponentTexture, TEXTUREGROUP_8BitData);
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetEditToolTextureData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData, TFunctionRef<UTexture2D*(ULandscapeComponent*)> GetComponentTexture)
|
|
{
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
uint8* SelectTextureData = NULL;
|
|
UTexture2D* EditToolTexture = nullptr;
|
|
if (Component)
|
|
{
|
|
EditToolTexture = GetComponentTexture(Component);
|
|
}
|
|
if (EditToolTexture)
|
|
{
|
|
TexDataInfo = GetTextureDataInfo(EditToolTexture);
|
|
SelectTextureData = (uint8*)TexDataInfo->GetMipData(0);
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1 - ComponentIndexX * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1 - ComponentIndexY * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2 - ComponentIndexX * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2 - ComponentIndexY * ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1 - SubsectionSizeQuads * SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1 - SubsectionSizeQuads * SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2 - SubsectionSizeQuads * SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2 - SubsectionSizeQuads * SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
int32 LandscapeX = SubIndexX * SubsectionSizeQuads + ComponentIndexX * ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY * SubsectionSizeQuads + ComponentIndexY * ComponentSizeQuads + SubY;
|
|
|
|
// Find the input data corresponding to this vertex
|
|
if (Component && SelectTextureData)
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = EditToolTexture->Source.GetSizeX();
|
|
int32 SizeV = EditToolTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * (float)SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * (float)SizeV);
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads + 1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads + 1) * SubIndexY + SubY;
|
|
uint8& TexData = SelectTextureData[TexX + TexY * SizeU];
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, TexData);
|
|
}
|
|
else
|
|
{
|
|
StoreData.Store(LandscapeX, LandscapeY, 0);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetEditToolTextureData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride, TFunctionRef<UTexture2D*&(ULandscapeComponent*)> GetComponentTexture, TextureGroup InTextureGroup)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
|
|
check(ComponentSizeQuads > 0);
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY);
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey);
|
|
if (Component == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UTexture2D*& EditToolTexture = GetComponentTexture(Component);
|
|
// if NULL, it was painted away
|
|
if (EditToolTexture == NULL)
|
|
{
|
|
// Construct Texture...
|
|
int32 WeightmapSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections;
|
|
EditToolTexture = Component->GetLandscapeProxy()->CreateLandscapeToolTexture(WeightmapSize, WeightmapSize, InTextureGroup, TSF_G8);
|
|
EditToolTexture->PostEditChange();
|
|
|
|
Component->UpdateEditToolRenderData();
|
|
}
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(EditToolTexture);
|
|
uint8* SelectTextureData = (uint8*)TexDataInfo->GetMipData(0);
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = EditToolTexture->Source.GetSizeX();
|
|
int32 SizeV = EditToolTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
checkSlow( LandscapeX >= X1 && LandscapeX <= X2 );
|
|
checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 );
|
|
|
|
// Find the input data corresponding to this vertex
|
|
int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1);
|
|
const uint8& Value = Data[DataIndex];
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
uint8& TexData = SelectTextureData[ TexX + TexY * SizeU ];
|
|
|
|
TexData = Value;
|
|
}
|
|
}
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
int32 TexX1 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX1;
|
|
int32 TexY1 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY1;
|
|
int32 TexX2 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX2;
|
|
int32 TexY2 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY2;
|
|
TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void FLandscapeEditDataInterface::SetXYOffsetDataTempl(int32 X1, int32 Y1, int32 X2, int32 Y2, const T* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
|
|
check(ComponentSizeQuads > 0);
|
|
// Find component range for this block of data
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
const FColor DefaultValue(128, 0, 128, 0);
|
|
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY);
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey);
|
|
|
|
UTexture2D* XYOffsetTexture = NULL;
|
|
if( Component==NULL)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (Component->XYOffsetmapTexture == NULL)
|
|
{
|
|
Component->Modify(GetShouldDirtyPackage());
|
|
//FlushRenderingCommands();
|
|
// Construct Texture...
|
|
int32 WeightmapSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections;
|
|
XYOffsetTexture = Component->GetLandscapeProxy()->CreateLandscapeTexture(WeightmapSize, WeightmapSize, TEXTUREGROUP_Terrain_Weightmap, TSF_BGRA8);
|
|
// Alloc dummy mips
|
|
const bool bClear = true;
|
|
ULandscapeComponent::CreateEmptyTextureMips(XYOffsetTexture, ELandscapeTextureUsage::FinalData, ELandscapeTextureType::Unknown, bClear);
|
|
|
|
// TODO: we flag somewhere that we need to use XYOffset Factory, so we can recompute shaders based on this change
|
|
|
|
XYOffsetTexture->PostEditChange();
|
|
|
|
//FlushRenderingCommands();
|
|
SetTextureValue(XYOffsetTexture, DefaultValue);
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(XYOffsetTexture);
|
|
int32 NumMips = XYOffsetTexture->Source.GetNumMips();
|
|
TArray<FColor*> TextureMipData;
|
|
TextureMipData.AddUninitialized(NumMips);
|
|
for( int32 MipIdx=0;MipIdx<NumMips;MipIdx++ )
|
|
{
|
|
TextureMipData[MipIdx] = (FColor*)TexDataInfo->GetMipData(MipIdx);
|
|
}
|
|
ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, XYOffsetTexture, TextureMipData, 0, 0, MAX_int32, MAX_int32, TexDataInfo);
|
|
|
|
Component->XYOffsetmapTexture = XYOffsetTexture;
|
|
FComponentReregisterContext ReregisterContext(Component);
|
|
}
|
|
else
|
|
{
|
|
XYOffsetTexture = Component->XYOffsetmapTexture;
|
|
}
|
|
}
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(XYOffsetTexture);
|
|
FColor* XYOffsetTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
|
|
// Find the texture data corresponding to this vertex
|
|
int32 SizeU = XYOffsetTexture->Source.GetSizeX();
|
|
int32 SizeV = XYOffsetTexture->Source.GetSizeY();
|
|
int32 WeightmapOffsetX = static_cast<int32>(Component->WeightmapScaleBias.Z * SizeU);
|
|
int32 WeightmapOffsetY = static_cast<int32>(Component->WeightmapScaleBias.W * SizeV);
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
checkSlow( LandscapeX >= X1 && LandscapeX <= X2 );
|
|
checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 );
|
|
|
|
// Find the input data corresponding to this vertex
|
|
int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1);
|
|
const T& Value = Data[DataIndex];
|
|
|
|
int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX;
|
|
int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY;
|
|
FColor& TexData = XYOffsetTextureData[ TexX + TexY * SizeU ];
|
|
|
|
uint16 XOffset = FMath::Clamp<uint16>(static_cast<uint16>(Value.X * LANDSCAPE_INV_XYOFFSET_SCALE + 32768.0), 0, 65535);
|
|
uint16 YOffset = FMath::Clamp<uint16>(static_cast<uint16>(Value.Y * LANDSCAPE_INV_XYOFFSET_SCALE + 32768.0), 0, 65535);
|
|
|
|
TexData.R = XOffset >> 8;
|
|
TexData.G = XOffset & 255;
|
|
TexData.B = YOffset >> 8;
|
|
TexData.A = YOffset & 255;
|
|
}
|
|
}
|
|
|
|
// Record the areas of the texture we need to re-upload
|
|
int32 TexX1 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX1;
|
|
int32 TexY1 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY1;
|
|
int32 TexX2 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX2;
|
|
int32 TexY2 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY2;
|
|
TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2);
|
|
}
|
|
}
|
|
|
|
// Update mipmaps
|
|
int32 NumMips = XYOffsetTexture->Source.GetNumMips();
|
|
TArray<FColor*> TextureMipData;
|
|
TextureMipData.AddUninitialized(NumMips);
|
|
for( int32 MipIdx=0;MipIdx<NumMips;MipIdx++ )
|
|
{
|
|
TextureMipData[MipIdx] = (FColor*)TexDataInfo->GetMipData(MipIdx);
|
|
}
|
|
ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, XYOffsetTexture, TextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector2D* Data, int32 Stride)
|
|
{
|
|
SetXYOffsetDataTempl<FVector2D>(X1, Y1, X2, Y2, Data, Stride);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector* Data, int32 Stride)
|
|
{
|
|
SetXYOffsetDataTempl<FVector>(X1, Y1, X2, Y2, Data, Stride);
|
|
}
|
|
|
|
FVector2D FLandscapeEditDataInterface::GetXYOffsetmapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData/* = NULL*/)
|
|
{
|
|
check(Component);
|
|
if (!TextureData && Component->XYOffsetmapTexture)
|
|
{
|
|
FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture);
|
|
TextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
}
|
|
|
|
if (TextureData)
|
|
{
|
|
int32 SizeU = Component->NumSubsections * (Component->SubsectionSizeQuads + 1);
|
|
int32 SizeV = Component->NumSubsections * (Component->SubsectionSizeQuads + 1);
|
|
|
|
int32 TexX = TexU;
|
|
int32 TexY = TexV;
|
|
FColor& TexData = TextureData[ TexX + TexY * SizeU ];
|
|
return FVector2D(((TexData.R * 256.0 + TexData.G) - 32768.0) * LANDSCAPE_XYOFFSET_SCALE, ((TexData.B * 256.0 + TexData.A) - 32768.0) * LANDSCAPE_XYOFFSET_SCALE );
|
|
}
|
|
return FVector2D::ZeroVector;
|
|
}
|
|
|
|
// XYOffset Interpolation version
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData)
|
|
{
|
|
// Copy variables
|
|
int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2;
|
|
ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN;
|
|
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
int32 ComponentSizeX = ComponentIndexX2 - ComponentIndexX1 + 1;
|
|
int32 ComponentSizeY = ComponentIndexY2 - ComponentIndexY1 + 1;
|
|
|
|
// Neighbor Components
|
|
ULandscapeComponent* BorderComponent[4] = { 0, 0, 0, 0 };
|
|
ULandscapeComponent* CornerComponent[4] = { 0, 0, 0, 0 };
|
|
bool NoBorderX1 = false, NoBorderX2 = false;
|
|
TArray<bool> NoBorderY1, NoBorderY2, ComponentDataExist;
|
|
TArray<ULandscapeComponent*> BorderComponentY1, BorderComponentY2;
|
|
ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY);
|
|
ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY);
|
|
bool bHasMissingValue = false;
|
|
|
|
FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = { 0, 0, 0, 0 };
|
|
FColor* NeighborXYOffsetmapTextureData[4] = { 0, 0, 0, 0 };
|
|
FVector2D CornerValues[4] = { FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector };
|
|
|
|
int32 EdgeCoord = (SubsectionSizeQuads + 1) * ComponentNumSubsections - 1; //ComponentSizeQuads;
|
|
|
|
TArray<FColor> EmptyXYOffset;
|
|
int32 XYOffsetSize = (LandscapeInfo->SubsectionSizeQuads + 1) * LandscapeInfo->ComponentNumSubsections;
|
|
XYOffsetSize = XYOffsetSize * XYOffsetSize;
|
|
EmptyXYOffset.Empty(XYOffsetSize);
|
|
for (int32 i = 0; i < XYOffsetSize; ++i)
|
|
{
|
|
EmptyXYOffset.Add(FColor(128, 0, 128, 0));
|
|
}
|
|
|
|
// initial loop....
|
|
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
|
|
{
|
|
NoBorderX1 = false;
|
|
NoBorderX2 = false;
|
|
BorderComponent[0] = BorderComponent[1] = NULL;
|
|
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
|
|
{
|
|
BorderComponent[2] = BorderComponent[3] = NULL;
|
|
int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY - ComponentIndexY1) + ComponentIndexX - ComponentIndexX1;
|
|
int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1;
|
|
int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1;
|
|
ComponentDataExist[ComponentIndexXY] = false;
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
FColor* XYOffsetmapTextureData = NULL;
|
|
uint8 CornerSet = 0;
|
|
bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ComponentIndexXX - 1 + ComponentIndexYY * ComponentSizeX];
|
|
bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ComponentIndexXX + (ComponentIndexYY - 1) * ComponentSizeX];
|
|
|
|
if (Component)
|
|
{
|
|
if (Component->XYOffsetmapTexture)
|
|
{
|
|
TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture);
|
|
XYOffsetmapTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
XYOffsetmapTextureData = EmptyXYOffset.GetData();
|
|
}
|
|
ComponentDataExist[ComponentIndexXY] = true;
|
|
// Update valid region
|
|
ValidX1 = FMath::Min<int32>(Component->GetSectionBase().X, ValidX1);
|
|
ValidX2 = FMath::Max<int32>(Component->GetSectionBase().X + ComponentSizeQuads, ValidX2);
|
|
ValidY1 = FMath::Min<int32>(Component->GetSectionBase().Y, ValidY1);
|
|
ValidY2 = FMath::Max<int32>(Component->GetSectionBase().Y + ComponentSizeQuads, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
if (!bHasMissingValue)
|
|
{
|
|
NoBorderY1.Empty(ComponentSizeX);
|
|
NoBorderY2.Empty(ComponentSizeX);
|
|
NoBorderY1.AddZeroed(ComponentSizeX);
|
|
NoBorderY2.AddZeroed(ComponentSizeX);
|
|
BorderComponentY1.Empty(ComponentSizeX);
|
|
BorderComponentY2.Empty(ComponentSizeX);
|
|
BorderComponentY1.AddZeroed(ComponentSizeX);
|
|
BorderComponentY2.AddZeroed(ComponentSizeX);
|
|
bHasMissingValue = true;
|
|
}
|
|
|
|
// Search for neighbor component for interpolation
|
|
bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX);
|
|
bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY);
|
|
// Search for left-closest component
|
|
if (bShouldSearchX || (!NoBorderX1 && !BorderComponent[0]))
|
|
{
|
|
NoBorderX1 = true;
|
|
for (int32 X = ComponentIndexX - 1; X >= ComponentIndexX1; X--)
|
|
{
|
|
BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, ComponentIndexY));
|
|
if (BorderComponent[0])
|
|
{
|
|
NoBorderX1 = false;
|
|
if (BorderComponent[0]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[0] = GetTextureDataInfo(BorderComponent[0]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[0] = (FColor*)NeighborTexDataInfo[0]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[0] = EmptyXYOffset.GetData();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for right-closest component
|
|
if (bShouldSearchX || (!NoBorderX2 && !BorderComponent[1]))
|
|
{
|
|
NoBorderX2 = true;
|
|
for (int32 X = ComponentIndexX + 1; X <= ComponentIndexX2; X++)
|
|
{
|
|
BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, ComponentIndexY));
|
|
if (BorderComponent[1])
|
|
{
|
|
NoBorderX2 = false;
|
|
if (BorderComponent[1]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[1] = GetTextureDataInfo(BorderComponent[1]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[1] = (FColor*)NeighborTexDataInfo[1]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[1] = EmptyXYOffset.GetData();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Search for up-closest component
|
|
if (bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX]))
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY - 1; Y >= ComponentIndexY1; Y--)
|
|
{
|
|
BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, Y));
|
|
if (BorderComponent[2])
|
|
{
|
|
NoBorderY1[ComponentIndexXX] = false;
|
|
if (BorderComponent[2]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[2] = EmptyXYOffset.GetData();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[2] = BorderComponentY1[ComponentIndexXX];
|
|
if (BorderComponent[2])
|
|
{
|
|
if (BorderComponent[2]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[2] = EmptyXYOffset.GetData();
|
|
}
|
|
|
|
}
|
|
}
|
|
// Search for bottom-closest component
|
|
if (bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX]))
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = true;
|
|
for (int32 Y = ComponentIndexY + 1; Y <= ComponentIndexY2; Y++)
|
|
{
|
|
BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, Y));
|
|
if (BorderComponent[3])
|
|
{
|
|
NoBorderY2[ComponentIndexXX] = false;
|
|
if (BorderComponent[3]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[3] = EmptyXYOffset.GetData();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BorderComponent[3] = BorderComponentY2[ComponentIndexXX];
|
|
if (BorderComponent[3])
|
|
{
|
|
if (BorderComponent[3]->XYOffsetmapTexture)
|
|
{
|
|
NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->XYOffsetmapTexture);
|
|
NeighborXYOffsetmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0);
|
|
}
|
|
else
|
|
{
|
|
NeighborXYOffsetmapTextureData[3] = EmptyXYOffset.GetData();
|
|
}
|
|
}
|
|
}
|
|
|
|
CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX - 1), (ComponentIndexY - 1))) : NULL;
|
|
CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX + 1), (ComponentIndexY - 1))) : NULL;
|
|
CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX - 1), (ComponentIndexY + 1))) : NULL;
|
|
CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ?
|
|
LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX + 1), (ComponentIndexY + 1))) : NULL;
|
|
|
|
if (CornerComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetXYOffsetmapData(CornerComponent[0], EdgeCoord, EdgeCoord);
|
|
}
|
|
else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads)
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads));
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, 0, NeighborXYOffsetmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1;
|
|
CornerValues[0] = GetXYOffsetmapData(BorderComponent[2], 0, EdgeCoord, NeighborXYOffsetmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetXYOffsetmapData(CornerComponent[1], 0, EdgeCoord);
|
|
}
|
|
else if (ExistUp && X2 >= (ComponentIndexX + 1)*ComponentSizeQuads)
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = FVector2D(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads));
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetXYOffsetmapData(BorderComponent[1], 0, 0, NeighborXYOffsetmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
CornerSet |= 1 << 1;
|
|
CornerValues[1] = GetXYOffsetmapData(BorderComponent[2], EdgeCoord, EdgeCoord, NeighborXYOffsetmapTextureData[2]);
|
|
}
|
|
|
|
if (CornerComponent[2])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetXYOffsetmapData(CornerComponent[2], EdgeCoord, 0);
|
|
}
|
|
else if (ExistLeft && Y2 >= (ComponentIndexY + 1)*ComponentSizeQuads) // Use data already stored for 0, 2
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads));
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, EdgeCoord, NeighborXYOffsetmapTextureData[0]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 2;
|
|
CornerValues[2] = GetXYOffsetmapData(BorderComponent[3], 0, 0, NeighborXYOffsetmapTextureData[3]);
|
|
}
|
|
|
|
if (CornerComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetXYOffsetmapData(CornerComponent[3], 0, 0);
|
|
}
|
|
else if (BorderComponent[1])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetXYOffsetmapData(BorderComponent[1], 0, EdgeCoord, NeighborXYOffsetmapTextureData[1]);
|
|
}
|
|
else if (BorderComponent[3])
|
|
{
|
|
CornerSet |= 1 << 3;
|
|
CornerValues[3] = GetXYOffsetmapData(BorderComponent[3], EdgeCoord, 0, NeighborXYOffsetmapTextureData[3]);
|
|
}
|
|
|
|
FillCornerValues(CornerSet, CornerValues);
|
|
ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet;
|
|
}
|
|
|
|
if (!ComponentDataExist[ComponentIndexXY])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1);
|
|
|
|
for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++)
|
|
{
|
|
for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++)
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for (int32 SubY = SubY1; SubY <= SubY2; SubY++)
|
|
{
|
|
for (int32 SubX = SubX1; SubX <= SubX2; SubX++)
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
// Find the input data corresponding to this vertex
|
|
if (Component)
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
FVector2D XYOffset = GetXYOffsetmapData(Component, (SubsectionSizeQuads + 1) * SubIndexX + SubX, (SubsectionSizeQuads + 1) * SubIndexY + SubY, XYOffsetmapTextureData);
|
|
StoreData.Store(LandscapeX, LandscapeY, XYOffset);
|
|
}
|
|
else
|
|
{
|
|
// Find the texture data corresponding to this vertex
|
|
FVector2D Value[4] = { FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector };
|
|
int32 Dist[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
|
|
FVector2D ValueX = FVector2D::ZeroVector, ValueY = FVector2D::ZeroVector;
|
|
bool Exist[4] = { false, false, false, false };
|
|
|
|
// Use data already stored for 0, 2
|
|
if (ExistLeft)
|
|
{
|
|
Value[0] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, LandscapeY));
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else if (BorderComponent[0])
|
|
{
|
|
Value[0] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborXYOffsetmapTextureData[0]);
|
|
Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 2))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY + 1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[0] = (Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2);
|
|
Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
Exist[0] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[1])
|
|
{
|
|
Value[1] = GetXYOffsetmapData(BorderComponent[1], 0, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborXYOffsetmapTextureData[1]);
|
|
Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
int32 Dist2 = ((ComponentIndexY + 1)*ComponentSizeQuads) - LandscapeY;
|
|
Value[1] = (Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2);
|
|
Dist[1] = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX;
|
|
Exist[1] = true;
|
|
}
|
|
}
|
|
|
|
if (ExistUp)
|
|
{
|
|
Value[2] = FVector2D(StoreData.Load(LandscapeX, ComponentIndexY*ComponentSizeQuads));
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else if (BorderComponent[2])
|
|
{
|
|
Value[2] = GetXYOffsetmapData(BorderComponent[2], (SubsectionSizeQuads + 1) * SubIndexX + SubX, EdgeCoord, NeighborXYOffsetmapTextureData[2]);
|
|
Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1) && (CornerSet & 1 << 1))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX;
|
|
Value[2] = (Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2);
|
|
Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads);
|
|
Exist[2] = true;
|
|
}
|
|
}
|
|
|
|
if (BorderComponent[3])
|
|
{
|
|
Value[3] = GetXYOffsetmapData(BorderComponent[3], (SubsectionSizeQuads + 1) * SubIndexX + SubX, 0, NeighborXYOffsetmapTextureData[3]);
|
|
Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
else
|
|
{
|
|
if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3))
|
|
{
|
|
int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads);
|
|
int32 Dist2 = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX;
|
|
Value[3] = (Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2);
|
|
Dist[3] = (ComponentIndexY + 1)*ComponentSizeQuads - LandscapeY;
|
|
Exist[3] = true;
|
|
}
|
|
}
|
|
|
|
CalcInterpValue<FVector2D>(Dist, Exist, Value, ValueX, ValueY);
|
|
|
|
FVector2D FinalValue = FVector2D::ZeroVector; // Default Value
|
|
if ((Exist[0] || Exist[1]) && (Exist[2] || Exist[3]))
|
|
{
|
|
FinalValue = CalcValueFromValueXY<FVector2D>(Dist, ValueX, ValueY, CornerSet, CornerValues);
|
|
}
|
|
else if ((BorderComponent[0] || BorderComponent[1]))
|
|
{
|
|
FinalValue = ValueX;
|
|
}
|
|
else if ((BorderComponent[2] || BorderComponent[3]))
|
|
{
|
|
FinalValue = ValueY;
|
|
}
|
|
else if ((Exist[0] || Exist[1]))
|
|
{
|
|
FinalValue = ValueX;
|
|
}
|
|
else if ((Exist[2] || Exist[3]))
|
|
{
|
|
FinalValue = ValueY;
|
|
}
|
|
|
|
StoreData.Store(LandscapeX, LandscapeY, FinalValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasMissingValue)
|
|
{
|
|
// generate something to fill in any missing data within the requested region
|
|
CalcMissingValues<FVector2D, TStoreData, FVector2D>(X1, X2, Y1, Y2,
|
|
ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2,
|
|
ComponentSizeX, ComponentSizeY, CornerValues,
|
|
NoBorderY1, NoBorderY2, ComponentDataExist, StoreData);
|
|
|
|
// clamp the returned bounds to the original request bounds
|
|
// this means the returned bounds are allowed to shrink (if there is only a small part of the request that actually exists)
|
|
// but it is not allowed to expand beyond the original request
|
|
ValidX1 = FMath::Max<int32>(X1, ValidX1);
|
|
ValidX2 = FMath::Min<int32>(X2, ValidX2);
|
|
ValidY1 = FMath::Max<int32>(Y1, ValidY1);
|
|
ValidY2 = FMath::Min<int32>(Y2, ValidY2);
|
|
}
|
|
else
|
|
{
|
|
// if there were no missing values, then the entire requested bounds is returned
|
|
ValidX1 = X1;
|
|
ValidX2 = X2;
|
|
ValidY1 = Y1;
|
|
ValidY2 = Y2;
|
|
}
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector2D* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
TArrayStoreData<FVector2D> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetXYOffsetDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, FVector2D>& SparseData)
|
|
{
|
|
TSparseStoreData<FVector2D> SparseStoreData(SparseData);
|
|
GetXYOffsetDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector* Data, int32 Stride)
|
|
{
|
|
if (Stride == 0)
|
|
{
|
|
Stride = (1 + X2 - X1);
|
|
}
|
|
TArrayStoreData<FVector> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetXYOffsetDataTempl(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap<FIntPoint, FVector>& SparseData)
|
|
{
|
|
TSparseStoreData<FVector> SparseStoreData(SparseData);
|
|
GetXYOffsetDataTempl(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
template<typename TStoreData>
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData)
|
|
{
|
|
int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2;
|
|
ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2);
|
|
|
|
for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ )
|
|
{
|
|
for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ )
|
|
{
|
|
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY));
|
|
|
|
FLandscapeTextureDataInfo* TexDataInfo = NULL;
|
|
FColor* OffsetTextureData = NULL;
|
|
if( Component && Component->XYOffsetmapTexture )
|
|
{
|
|
TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture);
|
|
OffsetTextureData = (FColor*)TexDataInfo->GetMipData(0);
|
|
}
|
|
|
|
// Find coordinates of box that lies inside component
|
|
int32 ComponentX1 = FMath::Clamp<int32>(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY1 = FMath::Clamp<int32>(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentX2 = FMath::Clamp<int32>(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
int32 ComponentY2 = FMath::Clamp<int32>(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads);
|
|
|
|
// Find subsection range for this box
|
|
int32 SubIndexX1 = FMath::Clamp<int32>((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections
|
|
int32 SubIndexY1 = FMath::Clamp<int32>((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexX2 = FMath::Clamp<int32>(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
int32 SubIndexY2 = FMath::Clamp<int32>(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1);
|
|
|
|
for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ )
|
|
{
|
|
for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ )
|
|
{
|
|
// Find coordinates of box that lies inside subsection
|
|
int32 SubX1 = FMath::Clamp<int32>(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY1 = FMath::Clamp<int32>(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
int32 SubX2 = FMath::Clamp<int32>(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads);
|
|
int32 SubY2 = FMath::Clamp<int32>(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads);
|
|
|
|
// Update texture data for the box that lies inside subsection
|
|
for( int32 SubY=SubY1;SubY<=SubY2;SubY++ )
|
|
{
|
|
for( int32 SubX=SubX1;SubX<=SubX2;SubX++ )
|
|
{
|
|
int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX;
|
|
int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY;
|
|
|
|
// Find the input data corresponding to this vertex
|
|
if( Component && OffsetTextureData )
|
|
{
|
|
FVector2D Value = GetXYOffsetmapData(Component, SubX, SubY, OffsetTextureData);
|
|
StoreData.Store(LandscapeX, LandscapeY, Value);
|
|
}
|
|
else
|
|
{
|
|
StoreData.Store(LandscapeX, LandscapeY, FVector2D(0.0f, 0.0f) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const ALandscape* FLandscapeEditDataInterface::GetTargetLandscape() const
|
|
{
|
|
return LandscapeInfo ? LandscapeInfo->LandscapeActor.Get() : nullptr;
|
|
}
|
|
|
|
bool FLandscapeEditDataInterface::CanHaveLandscapeLayersContent() const
|
|
{
|
|
return LandscapeInfo ? LandscapeInfo->CanHaveLayersContent() : false;
|
|
}
|
|
|
|
bool FLandscapeEditDataInterface::HasLandscapeLayersContent() const
|
|
{
|
|
const ALandscape* Landscape = GetTargetLandscape();
|
|
return Landscape ? Landscape->HasLayersContent() : false;
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector2D* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<FVector2D> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, FVector2D>& SparseData)
|
|
{
|
|
TSparseStoreData<FVector2D> SparseStoreData(SparseData);
|
|
GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector* Data, int32 Stride)
|
|
{
|
|
if( Stride==0 )
|
|
{
|
|
Stride = (1+X2-X1);
|
|
}
|
|
TArrayStoreData<FVector> ArrayStoreData(X1, Y1, Data, Stride);
|
|
GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData);
|
|
}
|
|
|
|
void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap<FIntPoint, FVector>& SparseData)
|
|
{
|
|
TSparseStoreData<FVector> SparseStoreData(SparseData);
|
|
GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, SparseStoreData);
|
|
}
|
|
|
|
//
|
|
// FLandscapeTextureDataInfo
|
|
//
|
|
|
|
FLandscapeTextureDataInfo::FLandscapeTextureDataInfo(UTexture2D* InTexture, bool bShouldDirtyPackage, ELandscapeTextureUsage InTextureUsage, ELandscapeTextureType InTextureType)
|
|
: Texture(InTexture)
|
|
, TextureUsage(InTextureUsage)
|
|
, TextureType(InTextureType)
|
|
{
|
|
MipInfo.AddZeroed(Texture->Source.GetNumMips());
|
|
Texture->SetFlags(RF_Transactional);
|
|
Texture->TemporarilyDisableStreaming();
|
|
Texture->Modify(bShouldDirtyPackage);
|
|
}
|
|
|
|
bool FLandscapeTextureDataInfo::UpdateTextureData()
|
|
{
|
|
const bool bCompressed = !Texture->CompressionNone;
|
|
|
|
bool bNeedToWaitForUpdate = false;
|
|
int32 DataSize = sizeof(FColor);
|
|
if (Texture->GetPixelFormat() == PF_G8)
|
|
{
|
|
DataSize = sizeof(uint8);
|
|
}
|
|
|
|
// Only wait once
|
|
bool bNeedToFinishCompilation = true;
|
|
|
|
for (int32 i = 0; i < MipInfo.Num(); i++)
|
|
{
|
|
if (MipInfo[i].MipData && MipInfo[i].MipUpdateRegions.Num() > 0)
|
|
{
|
|
if (bCompressed)
|
|
{
|
|
bNeedToWaitForUpdate = true;
|
|
// Cannot update regions on compressed textures so we will update the whole texture below.
|
|
break;
|
|
}
|
|
|
|
if (bNeedToFinishCompilation)
|
|
{
|
|
// Need to make sure we have a valid Resource
|
|
FTextureCompilingManager::Get().FinishCompilation({ Texture });
|
|
bNeedToFinishCompilation = false;
|
|
}
|
|
|
|
const uint32 SrcSizeX = (Texture->Source.GetSizeX()) >> i;
|
|
const uint32 SrcSizeY = (Texture->Source.GetSizeY()) >> i;
|
|
const uint32 SrcPitch = (SrcSizeX * DataSize);
|
|
const uint32 BufferSize = SrcSizeX * SrcSizeY * DataSize;
|
|
|
|
// Copy Mip update data so we can avoid waiting for Render thread in calling method
|
|
FMipInfo* CopyMipInfo = new FMipInfo();
|
|
CopyMipInfo->MipUpdateRegions = MipInfo[i].MipUpdateRegions;
|
|
CopyMipInfo->MipData = FMemory::Malloc(BufferSize);
|
|
FMemory::Memcpy(CopyMipInfo->MipData, MipInfo[i].MipData, BufferSize);
|
|
|
|
Texture->UpdateTextureRegions(i, CopyMipInfo->MipUpdateRegions.Num(), &CopyMipInfo->MipUpdateRegions[0], SrcPitch, DataSize, (uint8*)CopyMipInfo->MipData,
|
|
[CopyMipInfo](uint8* SrcData, const FUpdateTextureRegion2D*)
|
|
{
|
|
FMemory::Free(CopyMipInfo->MipData);
|
|
delete CopyMipInfo;
|
|
});
|
|
}
|
|
}
|
|
if (bNeedToWaitForUpdate)
|
|
{
|
|
Texture->UpdateResource();
|
|
}
|
|
|
|
return bNeedToWaitForUpdate;
|
|
}
|
|
|
|
FLandscapeTextureDataInfo::~FLandscapeTextureDataInfo()
|
|
{
|
|
// Unlock any mips still locked.
|
|
for( int32 i=0;i<MipInfo.Num();i++ )
|
|
{
|
|
if( MipInfo[i].MipData )
|
|
{
|
|
Texture->Source.UnlockMip(i);
|
|
MipInfo[i].MipData = NULL;
|
|
}
|
|
}
|
|
Texture->ClearFlags(RF_Transactional);
|
|
ULandscapeTextureHash::UpdateHash(Texture, TextureUsage, TextureType);
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|