做一个小工具来显示UE里 Landscape(启用编辑层)的高度图和权重图。
要显示高度图和权重图,本身是不难的。不过这里想要不修改引擎源码。所以是在外部(比如项目工程模块,或插件模块)做了事情。
可在 LandscapeComponent.h 中观察到数据结构。
在启用编辑层的情况下,数据在LayersData中,每个编辑层对应一个GUID。
UPROPERTY()
TMap<FGuid, FLandscapeLayerComponentData> LayersData;
每个 FLandscapeLayerComponentData包含一个FHeightmapData和一个FWeightmapData
USTRUCT(NotBlueprintable)
struct FLandscapeLayerComponentData
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
FHeightmapData HeightmapData;
UPROPERTY()
FWeightmapData WeightmapData;
bool IsInitialized() const { return HeightmapData.Texture != nullptr || WeightmapData.Textures.Num() > 0; }
};
FHeightmapData包含一个贴图:
USTRUCT(NotBlueprintable)
struct FHeightmapData
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
UTexture2D* Texture;
};
FWeightmapData包含多个贴图(因为可能有很多权重层):
USTRUCT(NotBlueprintable)
struct FWeightmapData
{
GENERATED_USTRUCT_BODY()
UPROPERTY()
TArray<UTexture2D*> Textures;
UPROPERTY()
TArray<FWeightmapLayerAllocationInfo> LayerAllocations;
UPROPERTY(Transient)
TArray<ULandscapeWeightmapUsage*> TextureUsages;
};
流程和之前一样,先定义一个UObject。
为了能在Detail面板看到相关的贴图,这里将原来的结构体换成了新的后缀为 _vis 的结构体,并将UProperty加上VisibleAnywhere。然后,提供一个按钮,也就是FindLandscapeTextures()函数,触发后找到世界中的地形,并将其数据复制过来。(目前,只观察第一个找到的地形的第一个Component)
代码如下:
LandscapeTextureWatcher.h
#pragma once
#include "UObject/Object.h"
#include "Landscape/Classes/LandscapeComponent.h"
#include "LandscapeTextureWatcher.generated.h"
USTRUCT(NotBlueprintable)
struct FWeightmapData_vis
{
GENERATED_USTRUCT_BODY()
UPROPERTY(VisibleAnywhere)
TArray<UTexture2D*> Textures;
UPROPERTY(VisibleAnywhere)
TArray<FWeightmapLayerAllocationInfo> LayerAllocations;
UPROPERTY(Transient)
TArray<ULandscapeWeightmapUsage*> TextureUsages;
};
USTRUCT(NotBlueprintable)
struct FHeightmapData_vis
{
GENERATED_USTRUCT_BODY()
UPROPERTY(VisibleAnywhere)
UTexture2D* Texture;
};
USTRUCT(NotBlueprintable)
struct FLandscapeLayerComponentData_vis
{
GENERATED_USTRUCT_BODY()
UPROPERTY(VisibleAnywhere)
FHeightmapData_vis HeightmapData;
UPROPERTY(VisibleAnywhere)
FWeightmapData_vis WeightmapData;
bool IsInitialized() const { return HeightmapData.Texture != nullptr || WeightmapData.Textures.Num() > 0; }
};
UCLASS(Blueprintable)
class ULandscapeTextureWatcher : public UObject
{
GENERATED_UCLASS_BODY()
public:
//按钮,点击后就把地形的纹理数据拿来
UFUNCTION(CallInEditor)
void FindLandscapeTextures();
//要观察的地形纹理数据:
UPROPERTY(VisibleAnywhere)
TMap<FGuid, FLandscapeLayerComponentData_vis> LayersData;
};
LandscapeTextureWatcher.cpp
#include "LandscapeTextureWatcher.h"
#include "Kismet/GameplayStatics.h"
#include "Editor.h"
#include "LandscapeInfo.h"
#include "Landscape/Classes/Landscape.h"
ULandscapeTextureWatcher::ULandscapeTextureWatcher(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{}
void ULandscapeTextureWatcher::FindLandscapeTextures()
{
//得到World
const UWorld* World = GEditor->GetEditorWorldContext().World();
//找地形:
TArray<AActor*> OutActors;
UGameplayStatics::GetAllActorsOfClass(World, ALandscape::StaticClass(), OutActors);
//没找到则直接返回
if (OutActors.Num() == 0)
return;
//找到则用第一个Landscape:
ALandscape* Landscape = (ALandscape*)OutActors[0];
if (Landscape->LandscapeComponents.Num() == 0)
return;
//使用第一个Component:
ULandscapeComponent* LandscapeComponent = Landscape->LandscapeComponents[0];
//遍历所有layer,填入数据
LandscapeComponent->ForEachLayer([&](const FGuid& LayerGuid, FLandscapeLayerComponentData& LayerData)
{
FLandscapeLayerComponentData_vis data;
data.HeightmapData.Texture = LayerData.HeightmapData.Texture;
data.WeightmapData.Textures = LayerData.WeightmapData.Textures;
LayersData.Add(LayerGuid, data);
});
}
然后,仿照之前第(3)步,显示其Detail面板。
开启编辑层功能创建地形,注意只创建一个Component(因为暂时只观察第一个Component)
创建两个编辑层,并且使用的地形材质有五个权重层。
结果如下:

虽然上面看到贴图是黑的,但是因为Alpha通道是0,可以直接打开贴图关闭alpha通道看效果。
高度图:

权重图,可以看出每层各占一个通道:
