參考了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行
