原文地址:https://www.cnblogs.com/LynnVon/p/11776482.html
参考了LandscapeEdModeComponentTool代码,魔改以后可在运行时动态增加LandscapeComponent,更换贴图,按需加载地图
主要是为了landscape的优越性能,LOD等
为实现无限地图提供了思路,只要把google的卫星地图动态加载进来,就可以实现无限大的真实地景
.c文件
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RuntimeGenerateTerrain.generated.h"
class ALandscapeProxy;
class UMaterialInstanceDynamic;
class UMaterialInstance;
class ULandscapeComponent;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class FLIGHTSIM_API URuntimeGenerateTerrain : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
URuntimeGenerateTerrain();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
bool LoadTexture(ULandscapeComponent* C);
void DynamicAddLandscapeComponent();
void SetXYtoComponentMap(ULandscapeComponent* C);
public:
ALandscapeProxy* mLandscape = nullptr;
UMaterialInstanceDynamic* GI;
UMaterialInstance* SounceMaterial;
TMap<FIntPoint, ULandscapeComponent*> mXYtoComponentMap;
private:
bool bAddComponent:1;
};
.cpp文件
URuntimeGenerateTerrain::URuntimeGenerateTerrain()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
bAddComponent = true;
// ...
static ConstructorHelpers::FObjectFinder<UMaterialInstance> _material(TEXT("MaterialInstanceConstant'/Game/GoogleMap/M_GoogleBASE_Inst.M_GoogleBASE_Inst'"));
if (_material.Succeeded())
{
SounceMaterial = _material.Object;
}
}
初始landscape更换贴图
void URuntimeGenerateTerrain::BeginPlay()
{
Super::BeginPlay();
// ...
UWorld* world = GetWorld();
check(world);
TArray<AActor*> _actor;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ALandscapeProxy::StaticClass(), _actor);
if (_actor.Num() > 0)
{
mLandscape = (ALandscapeProxy*)_actor[0];
}
if (mLandscape)
{
mLandscape->LandscapeMaterial = SounceMaterial;
for (ULandscapeComponent* C : mLandscape->LandscapeComponents)
{
LoadTexture(C);
SetXYtoComponentMap(C);
}
}
}
bool URuntimeGenerateTerrain::LoadTexture(ULandscapeComponent* C)
{
if (C->IsRenderStateCreated())
{
C->MarkRenderStateDirty();
FlushRenderingCommands();
}
for (int j = 0; j < C->MaterialInstances.Num(); j++)
{
if (!C->MaterialInstances[j]->IsA(UMaterialInstanceDynamic::StaticClass()))
{
C->MaterialInstances[j] = (UMaterialInstanceConstant*)UMaterialInstanceDynamic::Create(C->MaterialInstances[j], GetTransientPackage());// HACKY CAST!
}
UMaterialInstanceDynamic* MID = (UMaterialInstanceDynamic*)C->MaterialInstances[j];
int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1;
int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;
FString _fileName = FString("Texture2D'/Game/GoogleMap/satellite_en/Terrain_1/18/");
int rowOffset = FCString::Atoi(*UFS_Utils::GetTerrainConfigSection(FString("rowOffset"))); //UFS::Utils::GetTerrainConfigSection静态方法,获取config文件中定义的贴图初始的offset
int columnOffset = FCString::Atoi(*UFS_Utils::GetTerrainConfigSection(FString("columnOffset")));
_fileName.Append(FString::FromInt(XIndex + rowOffset));
_fileName.Append(FString("/"));
_fileName.Append(FString::FromInt(YIndex + columnOffset));
_fileName.Append(FString("."));
_fileName.Append(FString::FromInt(YIndex + columnOffset));
_fileName.Append(FString("'"));
UTexture2D* texture = LoadObject<UTexture2D>(NULL, *_fileName);
if (texture)
{
MID->SetTextureParameterValue(FName("Texture"), texture);
}
else
{
return false;
}
}
C->RecreateRenderState_Concurrent();
return true;
}
只需要把Google卫星地图编号,按照component的行号,列号对应相应的贴图,加载进来。
LandscapeComponent的行号,列号可以通过计算其相对位置获得,7是Quard数目
int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1; int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;
在runtime状态,无法获取LandscapeInfo,所以我们要自己去存储x,y索引号对应的landscapeComponent
void URuntimeGenerateTerrain::SetXYtoComponentMap(ULandscapeComponent* C)
{
int XIndex = C->GetRelativeTransform().GetLocation().X / 7 + 1;
int YIndex = C->GetRelativeTransform().GetLocation().Y / 7 + 1;
mXYtoComponentMap.Add(FIntPoint(XIndex, YIndex), C);
}
声明:
TMap<FIntPoint, ULandscapeComponent*> mXYtoComponentMap;
在动态增删LandscapeComponent要根据x,y索引来获取相应的Component
动态增加:
void URuntimeGenerateTerrain::DynamicAddLandscapeComponent()
{
if (!mLandscape)return;
//目前为硬编码做测试,后续这里是变量,动态改变
int ComponentIndexX1 = 0;
int ComponentIndexY1 = -1;
int ComponentIndexX2 = 8;
int ComponentIndexY2 = -1;
TArray<ULandscapeComponent*> NewComponents;
mLandscape->Modify();
for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++)
{
for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++)
{
ULandscapeComponent* LandscapeComponent = mXYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY));
if (!LandscapeComponent)
{
// Add New component...
FIntPoint ComponentBase = FIntPoint(ComponentIndexX, ComponentIndexY)*mLandscape->ComponentSizeQuads;
LandscapeComponent = NewObject<ULandscapeComponent>(mLandscape, NAME_None, RF_Transactional);
mLandscape->LandscapeComponents.Add(LandscapeComponent);
NewComponents.Add(LandscapeComponent);
LandscapeComponent->Init(
ComponentBase.X, ComponentBase.Y,
mLandscape->ComponentSizeQuads,
mLandscape->NumSubsections,
mLandscape->SubsectionSizeQuads
);
LandscapeComponent->AttachToComponent(mLandscape->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
//按理来说,landscapeComponent的相对位置z应该为0,但不知道什么原因,设置为0时,新增的component和landscape有高度差,只能通过一个一个试,最后确定256为正确值(目前不知道原因)
LandscapeComponent->SetRelativeLocation(FVector(ComponentBase.X, ComponentBase.Y, 256.f));
// Assign shared properties
LandscapeComponent->UpdatedSharedPropertiesFromActor();
int32 ComponentVerts = (mLandscape->SubsectionSizeQuads + 1) * mLandscape->NumSubsections;
// Update Weightmap Scale Bias
LandscapeComponent->WeightmapScaleBias = FVector4(1.0f / (float)ComponentVerts, 1.0f / (float)ComponentVerts, 0.5f / (float)ComponentVerts, 0.5f / (float)ComponentVerts);
LandscapeComponent->WeightmapSubsectionOffset = (float)(LandscapeComponent->SubsectionSizeQuads + 1) / (float)ComponentVerts;
TArray<FColor> HeightData;
HeightData.Empty(FMath::Square(ComponentVerts));
HeightData.AddZeroed(FMath::Square(ComponentVerts));
LandscapeComponent->InitHeightmapData(HeightData, true);
LandscapeComponent->UpdateMaterialInstances();
LandscapeComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
SetXYtoComponentMap(LandscapeComponent);
LoadTexture(LandscapeComponent);
}
}
}
// Need to register to use general height/xyoffset data update
for (int32 Idx = 0; Idx < NewComponents.Num(); Idx++)
{
NewComponents[Idx]->RegisterComponent();
}
//必须的 否则新增的component闪烁
for (ULandscapeComponent* NewComponent : NewComponents)
{
// Update Collision
NewComponent->UpdateCachedBounds();
NewComponent->UpdateBounds();
NewComponent->MarkRenderStateDirty();
}
}
因为ue4是不支持runtime landscape的,所以很多方法都是有check(GIsEditor)判定的,要修改源码,把这些Assert注释掉,否则会崩溃
具体有:
LandscapeEdit.cpp 208行 282行
Landscape.cpp 985
MaterialInstanceConstant.cpp 54行 78 行90行
MaterialInstance 3508行
浙公网安备 33010602011771号