Files
UnrealEngine/Engine/Source/Runtime/Landscape/Private/LandscapeDataAccess.cpp
2025-05-18 13:04:45 +08:00

206 lines
8.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LandscapeDataAccess.h"
#include "LandscapeComponent.h"
#if WITH_EDITOR
FLandscapeComponentDataInterfaceBase::FLandscapeComponentDataInterfaceBase(ULandscapeComponent* InComponent, int32 InMipLevel, bool InWorkOnEditingLayer):
MipLevel(InMipLevel)
{
UTexture2D* HeightMapTexture = InComponent->GetHeightmap(InWorkOnEditingLayer);
HeightmapStride = HeightMapTexture->Source.GetSizeX() >> InMipLevel;
HeightmapComponentOffsetX = FMath::RoundToInt((HeightMapTexture->Source.GetSizeX() >> InMipLevel) * InComponent->HeightmapScaleBias.Z);
HeightmapComponentOffsetY = FMath::RoundToInt((HeightMapTexture->Source.GetSizeY() >> InMipLevel) * InComponent->HeightmapScaleBias.W);
HeightmapSubsectionOffset = (InComponent->SubsectionSizeQuads + 1) >> InMipLevel;
ComponentSizeQuads = InComponent->ComponentSizeQuads;
ComponentSizeVerts = (InComponent->ComponentSizeQuads + 1) >> InMipLevel;
SubsectionSizeVerts = (InComponent->SubsectionSizeQuads + 1) >> InMipLevel;
ComponentNumSubsections = InComponent->NumSubsections;
}
LANDSCAPE_API FLandscapeComponentDataInterface::FLandscapeComponentDataInterface(ULandscapeComponent* InComponent, int32 InMipLevel, bool InWorkOnEditingLayer) :
FLandscapeComponentDataInterfaceBase(InComponent, InMipLevel, InWorkOnEditingLayer),
Component(InComponent),
bWorkOnEditingLayer(InWorkOnEditingLayer),
HeightMipData(NULL),
XYOffsetMipData(NULL)
{
UTexture2D* HeightMapTexture = Component->GetHeightmap(bWorkOnEditingLayer);
if (MipLevel < HeightMapTexture->Source.GetNumMips())
{
HeightMipData = (FColor*)DataInterface.LockMip(HeightMapTexture, MipLevel);
if (Component->XYOffsetmapTexture)
{
XYOffsetMipData = (FColor*)DataInterface.LockMip(Component->XYOffsetmapTexture, MipLevel);
}
}
}
LANDSCAPE_API FLandscapeComponentDataInterface::~FLandscapeComponentDataInterface()
{
if (HeightMipData)
{
UTexture2D* HeightMapTexture = Component->GetHeightmap(bWorkOnEditingLayer);
DataInterface.UnlockMip(HeightMapTexture, MipLevel);
if (Component->XYOffsetmapTexture)
{
DataInterface.UnlockMip(Component->XYOffsetmapTexture, MipLevel);
}
}
}
LANDSCAPE_API void FLandscapeComponentDataInterface::GetHeightmapTextureData(TArray<FColor>& OutData, bool bOkToFail)
{
if (bOkToFail && !HeightMipData)
{
OutData.Empty();
return;
}
#if LANDSCAPE_VALIDATE_DATA_ACCESS
check(HeightMipData);
#endif
int32 HeightmapSize = ((Component->SubsectionSizeQuads + 1) * Component->NumSubsections) >> MipLevel;
OutData.Empty(FMath::Square(HeightmapSize));
OutData.AddUninitialized(FMath::Square(HeightmapSize));
for (int32 SubY = 0; SubY < HeightmapSize; SubY++)
{
// X/Y of the vertex we're looking at in component's coordinates.
int32 CompY = SubY;
// UV coordinates of the data offset into the texture
int32 TexV = SubY + HeightmapComponentOffsetY;
// Copy the data
FMemory::Memcpy(&OutData[CompY * HeightmapSize], &HeightMipData[HeightmapComponentOffsetX + TexV * HeightmapStride], HeightmapSize * sizeof(FColor));
}
}
LANDSCAPE_API bool FLandscapeComponentDataInterface::GetWeightmapTextureData(ULandscapeLayerInfoObject* InLayerInfo, TArray<uint8>& OutData, bool bInUseEditingWeightmap, bool bInRemoveSubsectionDuplicates)
{
int32 LayerIdx = INDEX_NONE;
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(bInUseEditingWeightmap);
const TArray<UTexture2D*>& ComponentWeightmapTextures = Component->GetWeightmapTextures(bInUseEditingWeightmap);
for (int32 Idx = 0; Idx < ComponentWeightmapLayerAllocations.Num(); Idx++)
{
if (ComponentWeightmapLayerAllocations[Idx].LayerInfo == InLayerInfo)
{
LayerIdx = Idx;
break;
}
}
if (LayerIdx < 0)
{
return false;
}
if (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex >= ComponentWeightmapTextures.Num())
{
return false;
}
if (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel >= 4)
{
return false;
}
// If requested to skip the duplicate row/col of texture data
int32 WeightmapSize = bInRemoveSubsectionDuplicates ?
((Component->SubsectionSizeQuads * Component->NumSubsections) + 1) >> MipLevel :
((Component->SubsectionSizeQuads + 1) * Component->NumSubsections) >> MipLevel;
OutData.Empty(FMath::Square(WeightmapSize));
OutData.AddUninitialized(FMath::Square(WeightmapSize));
// DataInterface Lock is a LockMipReadOnly on the texture
const FColor* WeightMipData = (const FColor*)DataInterface.LockMip(ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel);
// Channel remapping
int32 ChannelOffsets[4] = { (int32)STRUCT_OFFSET(FColor, R), (int32)STRUCT_OFFSET(FColor, G), (int32)STRUCT_OFFSET(FColor, B), (int32)STRUCT_OFFSET(FColor, A) };
const uint8* SrcTextureData = (const uint8*)WeightMipData + ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel];
for (int32 i = 0; i < FMath::Square(WeightmapSize); i++)
{
// If removing subsection duplicates, convert vertex to texel index
OutData[i] = bInRemoveSubsectionDuplicates ? SrcTextureData[VertexIndexToTexel(i) * sizeof(FColor)] : SrcTextureData[i * sizeof(FColor)];
}
DataInterface.UnlockMip(ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel);
return true;
}
LANDSCAPE_API FColor* FLandscapeComponentDataInterface::GetXYOffsetData(int32 LocalX, int32 LocalY) const
{
#if LANDSCAPE_VALIDATE_DATA_ACCESS
check(Component);
check(LocalX >= 0 && LocalY >= 0 && LocalX < ComponentSizeQuads + 1 && LocalY < ComponentSizeQuads + 1);
#endif
const int32 WeightmapSize = ((Component->SubsectionSizeQuads + 1) * Component->NumSubsections) >> MipLevel;
int32 SubNumX;
int32 SubNumY;
int32 SubX;
int32 SubY;
ComponentXYToSubsectionXY(LocalX, LocalY, SubNumX, SubNumY, SubX, SubY);
return &XYOffsetMipData[SubX + SubNumX*SubsectionSizeVerts + (SubY + SubNumY*SubsectionSizeVerts)*WeightmapSize];
}
LANDSCAPE_API FVector FLandscapeComponentDataInterface::GetLocalVertex(int32 LocalX, int32 LocalY) const
{
const float ScaleFactor = (float)ComponentSizeQuads / (float)(ComponentSizeVerts - 1);
float XOffset, YOffset;
GetXYOffset(LocalX, LocalY, XOffset, YOffset);
return FVector(LocalX * ScaleFactor + XOffset, LocalY * ScaleFactor + YOffset, LandscapeDataAccess::GetLocalHeight(GetHeight(LocalX, LocalY)));
}
LANDSCAPE_API FVector FLandscapeComponentDataInterface::GetWorldVertex(int32 LocalX, int32 LocalY) const
{
return Component->GetComponentTransform().TransformPosition(GetLocalVertex(LocalX, LocalY));
}
LANDSCAPE_API void FLandscapeComponentDataInterface::GetWorldTangentVectors(int32 LocalX, int32 LocalY, FVector& WorldTangentX, FVector& WorldTangentY, FVector& WorldTangentZ) const
{
FColor* Data = GetHeightData(LocalX, LocalY);
WorldTangentZ = LandscapeDataAccess::UnpackNormal(*Data);
WorldTangentX = FVector(-WorldTangentZ.Z, 0.f, WorldTangentZ.X);
WorldTangentY = FVector(0.f, WorldTangentZ.Z, -WorldTangentZ.Y);
WorldTangentX = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentX);
WorldTangentY = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentY);
WorldTangentZ = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentZ);
}
LANDSCAPE_API void FLandscapeComponentDataInterface::GetWorldPositionTangents(int32 LocalX, int32 LocalY, FVector& WorldPos, FVector& WorldTangentX, FVector& WorldTangentY, FVector& WorldTangentZ) const
{
FColor* Data = GetHeightData(LocalX, LocalY);
WorldTangentZ = LandscapeDataAccess::UnpackNormal(*Data);
WorldTangentX = FVector(WorldTangentZ.Z, 0.f, -WorldTangentZ.X);
WorldTangentY = WorldTangentZ ^ WorldTangentX;
float Height = LandscapeDataAccess::UnpackHeight(*Data);
const float ScaleFactor = (float)ComponentSizeQuads / (float)(ComponentSizeVerts - 1);
float XOffset, YOffset;
GetXYOffset(LocalX, LocalY, XOffset, YOffset);
WorldPos = Component->GetComponentTransform().TransformPosition(FVector(LocalX * ScaleFactor + XOffset, LocalY * ScaleFactor + YOffset, Height));
WorldTangentX = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentX);
WorldTangentY = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentY);
WorldTangentZ = Component->GetComponentTransform().TransformVectorNoScale(WorldTangentZ);
}
int32 FLandscapeComponentDataInterface::GetHeightmapSizeX(int32 MipIndex) const
{
return Component->GetHeightmap(bWorkOnEditingLayer)->Source.GetSizeX() >> MipIndex;
}
int32 FLandscapeComponentDataInterface::GetHeightmapSizeY(int32 MipIndex) const
{
return Component->GetHeightmap(bWorkOnEditingLayer)->Source.GetSizeY() >> MipIndex;
}
#endif // WITH_EDITOR