zoukankan      html  css  js  c++  java
  • UE4 Runtime Landscape

    原文地址: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行

      

      

     
  • 相关阅读:
    os.fork()
    解决方案:WindowsError: [Error 2]
    Python遍历文件夹和读写文件的方法
    导航帖
    IDEA后缀补全及快捷键
    Codeforces-Round#614 Div2
    图论算法-欧拉回路 专题训练
    快速求出n!质因数的个数
    Codeforces-Round#589 Div2
    洛谷P3386二分图匹配
  • 原文地址:https://www.cnblogs.com/LynnVon/p/11776482.html
Copyright © 2011-2022 走看看