UE4 Runtime Landscape

房东的猫 提交于 2019-12-03 04:16:47

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

 

  

  

 

 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!