Files
UnrealEngine/Engine/Source/Runtime/Landscape/Public/LandscapeRender.h
2025-05-18 13:04:45 +08:00

1217 lines
42 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LandscapeRender.h: New terrain rendering
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "Misc/Guid.h"
#include "Engine/EngineTypes.h"
#include "Templates/RefCounting.h"
#include "Containers/ArrayView.h"
#include "ShaderParameters.h"
#include "RenderResource.h"
#include "UniformBuffer.h"
#include "VertexFactory.h"
#include "MaterialShared.h"
#include "Materials/MaterialRenderProxy.h"
#include "LandscapeProxy.h"
#include "RendererInterface.h"
#include "MeshBatch.h"
#include "SceneManagement.h"
#include "Engine/MapBuildDataRegistry.h"
#include "LandscapeComponent.h"
#include "Materials/MaterialInterface.h"
#include "PrimitiveViewRelevance.h"
#include "PrimitiveSceneProxy.h"
#include "StaticMeshResources.h"
#include "StaticMeshSceneProxy.h"
#include "SceneViewExtension.h"
#include "Rendering/CustomRenderPass.h"
#include "Tasks/Task.h"
#define LANDSCAPE_LOD_LEVELS 8
#define LANDSCAPE_MAX_SUBSECTION_NUM 2
class FLandscapeComponentSceneProxy;
enum EShaderPlatform : uint16;
#if WITH_EDITOR
class ULandscapeEditResourcesSubsystem;
#endif
namespace UE::Renderer::Private { class IShadowInvalidatingInstances; }
#if RHI_RAYTRACING
struct FLandscapeRayTracingImpl;
#endif
#if WITH_EDITOR
namespace ELandscapeViewMode
{
enum Type: int32
{
Invalid = -1,
/** Color only */
Normal = 0,
EditLayer,
/** Layer debug only */
DebugLayer,
LayerDensity,
LayerUsage,
LOD,
WireframeOnTop,
LayerContribution
};
}
extern LANDSCAPE_API int32 GLandscapeViewMode;
namespace ELandscapeEditRenderMode
{
enum Type
{
None = 0x0,
Gizmo = 0x1,
SelectRegion = 0x2,
SelectComponent = 0x4,
Select = SelectRegion | SelectComponent,
Mask = 0x8,
InvertedMask = 0x10, // Should not be overlapped with other bits
BitMaskForMask = Mask | InvertedMask,
};
}
LANDSCAPE_API extern bool GLandscapeEditModeActive;
LANDSCAPE_API extern int32 GLandscapeEditRenderMode;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetLayerDebugColorMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GLayerDebugColorMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetSelectionColorMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GSelectionColorMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetSelectionRegionMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GSelectionRegionMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetMaskRegionMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GMaskRegionMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetColorMaskRegionMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GColorMaskRegionMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetLandscapeBlackTexture")
LANDSCAPE_API extern TObjectPtr<UTexture2D> GLandscapeBlackTexture;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetLandscapeLayerUsageMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GLandscapeLayerUsageMaterial;
UE_DEPRECATED(5.6, "This global has been deprecated. Please use ULandscapeEditResourcesSubsystem->GetLandscapeDirtyMaterial")
LANDSCAPE_API extern TObjectPtr<UMaterialInterface> GLandscapeDirtyMaterial;
#endif
namespace UE::Landscape
{
bool NeedsFixedGridVertexFactory(EShaderPlatform InShaderPlatform);
bool ShouldBuildGrassMapRenderingResources();
} // namespace UE::Landscape
/** The uniform shader parameters for a landscape draw call. */
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FLandscapeUniformShaderParameters, LANDSCAPE_API)
SHADER_PARAMETER(int32, ComponentBaseX)
SHADER_PARAMETER(int32, ComponentBaseY)
SHADER_PARAMETER(int32, SubsectionSizeVerts)
SHADER_PARAMETER(int32, NumSubsections)
SHADER_PARAMETER(int32, RayTracingSectionSizeVerts)
SHADER_PARAMETER(int32, NumRayTracingSections)
SHADER_PARAMETER(int32, RayTracingLODBias)
SHADER_PARAMETER(int32, LastLOD)
SHADER_PARAMETER(uint32, VirtualTexturePerPixelHeight)
SHADER_PARAMETER(float, InvLODBlendRange)
SHADER_PARAMETER(float, NonNaniteVirtualShadowMapConstantDepthBias)
SHADER_PARAMETER(FVector4f, HeightmapTextureSize)
SHADER_PARAMETER(FVector4f, HeightmapUVScaleBias)
SHADER_PARAMETER(FVector4f, WeightmapUVScaleBias)
SHADER_PARAMETER(FVector4f, LandscapeLightmapScaleBias)
SHADER_PARAMETER(FVector4f, SubsectionSizeVertsLayerUVPan)
SHADER_PARAMETER(FVector4f, SubsectionOffsetParams)
SHADER_PARAMETER(FVector4f, LightmapSubsectionOffsetParams)
SHADER_PARAMETER(FMatrix44f, LocalToWorldNoScaling)
SHADER_PARAMETER_TEXTURE(Texture2D, HeightmapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, HeightmapTextureSampler)
SHADER_PARAMETER_TEXTURE(Texture2D, NormalmapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, NormalmapTextureSampler)
SHADER_PARAMETER_TEXTURE(Texture2D, XYOffsetmapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, XYOffsetmapTextureSampler)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FLandscapeVertexFactoryMVFParameters, LANDSCAPE_API)
SHADER_PARAMETER(FIntPoint, SubXY)
SHADER_PARAMETER(FIntPoint, RayTracingSectionXY)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
typedef TUniformBufferRef<FLandscapeVertexFactoryMVFParameters> FLandscapeVertexFactoryMVFUniformBufferRef;
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FLandscapeSectionLODUniformParameters, LANDSCAPE_API)
SHADER_PARAMETER(int32, LandscapeIndex)
SHADER_PARAMETER(FIntPoint, Min)
SHADER_PARAMETER(FIntPoint, Size)
SHADER_PARAMETER_SRV(Buffer<float>, SectionLODBias)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FLandscapeFixedGridUniformShaderParameters, LANDSCAPE_API)
SHADER_PARAMETER(FVector4f, LodValues)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
/* Data needed for the landscape vertex factory to set the render state for an individual batch element */
struct FLandscapeBatchElementParams
{
#if RHI_RAYTRACING
FRHIUniformBuffer* LandscapeVertexFactoryMVFUniformBuffer;
#endif
const TUniformBuffer<FLandscapeUniformShaderParameters>* LandscapeUniformShaderParametersResource;
const TArray<TUniformBuffer<FLandscapeFixedGridUniformShaderParameters>>* FixedGridUniformShaderParameters;
FUniformBufferRHIRef LandscapeSectionLODUniformParameters;
const FLandscapeComponentSceneProxy* SceneProxy;
int32 CurrentLOD;
};
class FLandscapeElementParamArray : public FOneFrameResource
{
public:
TArray<FLandscapeBatchElementParams, SceneRenderingAllocator> ElementParams;
};
class FLandscapeVertexFactoryVertexShaderParameters : public FVertexFactoryShaderParameters
{
DECLARE_TYPE_LAYOUT(FLandscapeVertexFactoryVertexShaderParameters, NonVirtual);
public:
/**
* Bind shader constants by name
* @param ParameterMap - mapping of named shader constants to indices
*/
void Bind(const FShaderParameterMap& ParameterMap)
{
}
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const FSceneView* InView,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const FVertexFactory* VertexFactory,
const FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams
) const;
};
/** Pixel shader parameters for use with FLandscapeVertexFactory */
class FLandscapeVertexFactoryPixelShaderParameters : public FVertexFactoryShaderParameters
{
DECLARE_TYPE_LAYOUT(FLandscapeVertexFactoryPixelShaderParameters, NonVirtual);
public:
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const FSceneView* InView,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const FVertexFactory* VertexFactory,
const FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams
) const;
};
/** vertex factory for VTF-heightmap terrain */
class FLandscapeVertexFactory : public FVertexFactory
{
DECLARE_VERTEX_FACTORY_TYPE_API(FLandscapeVertexFactory, LANDSCAPE_API);
public:
LANDSCAPE_API FLandscapeVertexFactory(ERHIFeatureLevel::Type InFeatureLevel);
virtual ~FLandscapeVertexFactory()
{
// can only be destroyed from the render thread
ReleaseResource();
}
struct FDataType
{
/** The stream to read the vertex position from. */
FVertexStreamComponent PositionComponent;
};
/**
* Should we cache the material's shadertype on this platform with this vertex factory?
*/
static LANDSCAPE_API bool ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters);
/**
* Can be overridden by FVertexFactory subclasses to modify their compile environment just before compilation occurs.
*/
static LANDSCAPE_API void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
/**
* Get vertex elements used when during PSO precaching materials using this vertex factory type
*/
static LANDSCAPE_API void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);
/**
* Copy the data from another vertex factory
* @param Other - factory to copy from
*/
LANDSCAPE_API void Copy(const FLandscapeVertexFactory& Other);
// FRenderResource interface.
LANDSCAPE_API virtual void InitRHI(FRHICommandListBase& RHICmdList) override;
virtual void ReleaseResource() override final { FVertexFactory::ReleaseResource(); }
/**
* An implementation of the interface used by TSynchronizedResource to update the resource with new data from the game thread.
*/
void SetData(const FDataType& InData)
{
Data = InData;
UpdateRHI(FRHICommandListImmediate::Get());
}
/** stream component data bound to this vertex factory */
FDataType Data;
};
/** vertex factory for VTF-heightmap terrain */
class FLandscapeXYOffsetVertexFactory : public FLandscapeVertexFactory
{
DECLARE_VERTEX_FACTORY_TYPE_API(FLandscapeXYOffsetVertexFactory, LANDSCAPE_API);
public:
FLandscapeXYOffsetVertexFactory(ERHIFeatureLevel::Type InFeatureLevel)
: FLandscapeVertexFactory(InFeatureLevel)
{
}
virtual ~FLandscapeXYOffsetVertexFactory() {}
static void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
};
/** Vertex factory for fixed grid runtime virtual texture lod */
class FLandscapeFixedGridVertexFactory : public FLandscapeVertexFactory
{
DECLARE_VERTEX_FACTORY_TYPE_API(FLandscapeFixedGridVertexFactory, LANDSCAPE_API);
public:
FLandscapeFixedGridVertexFactory(ERHIFeatureLevel::Type InFeatureLevel)
: FLandscapeVertexFactory(InFeatureLevel)
{
}
static LANDSCAPE_API void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
};
struct FLandscapeVertex
{
uint8 VertexX;
uint8 VertexY;
uint8 SubX;
uint8 SubY;
};
//
// FLandscapeVertexBuffer
//
class FLandscapeVertexBuffer final : public FVertexBuffer
{
ERHIFeatureLevel::Type FeatureLevel;
int32 NumVertices;
int32 SubsectionSizeVerts;
int32 NumSubsections;
public:
/** Constructor. */
FLandscapeVertexBuffer(FRHICommandListBase& RHICmdList, ERHIFeatureLevel::Type InFeatureLevel, int32 InNumVertices, int32 InSubsectionSizeVerts, int32 InNumSubsections, const FName& InOwnerName)
: FeatureLevel(InFeatureLevel)
, NumVertices(InNumVertices)
, SubsectionSizeVerts(InSubsectionSizeVerts)
, NumSubsections(InNumSubsections)
{
SetOwnerName(InOwnerName);
InitResource(RHICmdList);
}
/** Destructor. */
virtual ~FLandscapeVertexBuffer()
{
ReleaseResource();
}
/**
* Initialize the RHI for this rendering resource
*/
virtual void InitRHI(FRHICommandListBase& RHICmdList) override;
};
//
// FLandscapeSharedBuffers
//
class FLandscapeSharedBuffers : public FRefCountedObject
{
public:
struct FLandscapeIndexRanges
{
int32 MinIndex[LANDSCAPE_MAX_SUBSECTION_NUM][LANDSCAPE_MAX_SUBSECTION_NUM];
int32 MaxIndex[LANDSCAPE_MAX_SUBSECTION_NUM][LANDSCAPE_MAX_SUBSECTION_NUM];
int32 MinIndexFull;
int32 MaxIndexFull;
};
int32 NumVertices;
int32 SharedBuffersKey;
int32 NumIndexBuffers;
int32 SubsectionSizeVerts;
int32 NumSubsections;
int32 NumRayTracingSections;
FLandscapeVertexFactory* VertexFactory;
FLandscapeVertexFactory* FixedGridVertexFactory;
FLandscapeVertexBuffer* VertexBuffer;
TArray<TUniformBuffer<FLandscapeFixedGridUniformShaderParameters>> LandscapeFixedGridUniformShaderParameters;
FRenderResource* TileMesh;
FLandscapeVertexFactory* TileVertexFactory;
FVertexBuffer* TileDataBuffer;
// array per mip level, storing FIndexBuffer pointers
FIndexBuffer** IndexBuffers;
// array per mip level, storing index buffer ranges per subssection, and for the entire component
FLandscapeIndexRanges* IndexRanges;
bool bUse32BitIndices;
FIndexBuffer* GrassIndexBuffer;
TArray<int32, TInlineAllocator<8>> GrassIndexMipOffsets;
#if RHI_RAYTRACING
TArray<FIndexBuffer*> ZeroOffsetIndexBuffers;
#endif
UE_DEPRECATED(5.6, "This constructor has been deprecated. Please use constructor which also provides the number of raytracing sections.")
LANDSCAPE_API FLandscapeSharedBuffers(FRHICommandListBase& RHICmdList, int32 SharedBuffersKey, int32 SubsectionSizeQuads, int32 NumSubsections, ERHIFeatureLevel::Type FeatureLevel, const FName& OwnerName = NAME_None);
LANDSCAPE_API FLandscapeSharedBuffers(FRHICommandListBase& RHICmdList, int32 SharedBuffersKey, int32 SubsectionSizeQuads, int32 NumSubsections, int32 NumRayTracingSections, ERHIFeatureLevel::Type FeatureLevel, const FName& OwnerName = NAME_None);
template <typename INDEX_TYPE>
void CreateIndexBuffers(FRHICommandListBase& RHICmdList, const FName& OwnerName);
template <typename INDEX_TYPE>
void CreateGrassIndexBuffer(FRHICommandListBase& RHICmdList, const FName& InOwnerName);
LANDSCAPE_API virtual ~FLandscapeSharedBuffers();
};
//
// FLandscapeSectionInfo
//
class FLandscapeSectionInfo : public TIntrusiveLinkedList<FLandscapeSectionInfo>
{
public:
FLandscapeSectionInfo(const UWorld* InWorld, const FGuid& InLandscapeGuid, const FIntPoint& InComponentBase, uint32 LODGroupKey, uint32 InLandscapeKey);
virtual ~FLandscapeSectionInfo() = default;
virtual float ComputeLODForView(const FSceneView& InView) const = 0;
virtual float ComputeLODBias() const = 0;
virtual int32 GetSectionPriority() const { return INDEX_NONE; }
virtual const FPrimitiveSceneInfo* GetPrimitiveSceneInfo() const = 0;
/** Computes the worldspace units per vertex of the landscape section. */
virtual double ComputeSectionResolution() const { return -1.0; }
virtual void GetSectionBoundsAndLocalToWorld(FBoxSphereBounds& LocalBounds, FMatrix& LocalToWorld) const = 0;
virtual void GetSectionCenterAndVectors(FVector& OutSectionCenterWorldSpace, FVector& OutSectionXVectorWorldSpace, FVector& OutSectionYVectorWorldSpace) const = 0;
/* return the resolution of a component, in vertices (-1 for any sections that are not grid based, i.e. mesh sections) */
virtual int32 GetComponentResolution() const { return -1; }
/* Used to notify derived classes when render coords are calculated */
virtual void OnRenderCoordsChanged(FRHICommandListBase& RHICmdList) = 0;
virtual bool ShouldInvalidateShadows(const FSceneView& InView, float InLODValue, float InLastShadowInvalidationLODValue) const { return false; }
public:
// A hash of the world and (LandscapeGUID or LOD Group Key)
uint32 LandscapeKey = 0;
// LOD Group Key (0 if no group)
uint32 LODGroupKey = 0;
// Coordinate in the RenderSystem
FIntPoint RenderCoord = FIntPoint(INT32_MIN, INT32_MIN);
// Component base coordinate (relative to the ALandscape actor)
FIntPoint ComponentBase = FIntPoint(ForceInit);
// Scene that this landscape section belongs to
FSceneInterface* Scene = nullptr;
bool bResourcesCreated = false;
bool bRegistered = false;
};
//
// FLandscapeRenderSystem
//
struct FLandscapeRenderSystem
{
typedef uint32 FViewKey;
struct LODSettingsComponent
{
float LOD0ScreenSizeSquared;
float LOD1ScreenSizeSquared;
float LODOnePlusDistributionScalarSquared;
float LastLODScreenSizeSquared;
float VirtualShadowMapInvalidationLimitLOD;
int8 LastLODIndex;
int8 ForcedLOD;
int8 DrawCollisionPawnLOD;
int8 DrawCollisionVisibilityLOD;
};
static float ComputeLODFromScreenSize(const LODSettingsComponent& InLODSettings, float InScreenSizeSquared);
static TBitArray<> LandscapeIndexAllocator;
int32 LandscapeIndex;
FIntPoint Min;
FIntPoint Size;
TResourceArray<float> SectionLODBiases;
TArray<FLandscapeSectionInfo*> SectionInfos;
// Number of sections with resources created
int32 ReferenceCount;
// Number of sections registered
int32 RegisteredCount;
FBufferRHIRef SectionLODBiasBuffer;
FShaderResourceViewRHIRef SectionLODBiasSRV;
FUniformBufferRHIRef SectionLODUniformBuffer;
// For a given view, we use those 2 maps to store, for each FLandscapeSectionInfo in SectionInfos, the current LOD Values so that it can be passed down to the renderer
// These are only really valid for a given render since the list of sections can change in the next frame.
// The first map is for views that don't have a persistent view state
TMap<const FSceneView*, TResourceArray<float>> PerViewCachedSectionLODValues; // Key = view to render, Value = linear list of LOD values for each FLandscapeSectionInfo
TMap<uint32, TResourceArray<float>> PerViewStateCachedSectionLODValues; // Key = view state key corresponding to the view to render, Value = linear list of LOD values for each FLandscapeSectionInfo
// This map allows to track, for each FLandscapeSectionInfo that triggered a shadow invalidation, the LOD value that was used when the invalidation occurred
// Key = SectionInfo's RenderCoord (we cannot use a linear index like the TMaps above because these might change as new sections get added/removed dynamically and this needs to be tracked across frames)
// Value = LOD value of the section when the last shadow invalidation occurred
using SectionKeyToLODValueMap = TMap<FIntPoint, float>;
TMap<uint32, SectionKeyToLODValueMap> PerViewStateLastShadowInvalidationSectionLODValues; // Key = view state key corresponding to the view to render, Value = see above
// List of shadow invalidations to perform this frame (because IShadowInvalidatingInstances is not parallel-render thread-safe, we have to store them temporarily and issue them on the render thread later on) :
TMap<UE::Renderer::Private::IShadowInvalidatingInstances*, TArray<const FPrimitiveSceneInfo*>> ShadowInvalidationRequests;
/** Forced LOD level which overrides the ForcedLOD level of all the sections under this LandscapeRenderSystem. */
int8 ForcedLODOverride;
// Resolution, Origin and Size, for use in LOD Groups to verify that all landscapes are of matching resolutions, orientation and scale
int32 ComponentResolution = -1;
FVector ComponentOrigin = FVector::ZeroVector; // world space position of the center of the origin component (render coord 0,0)
FVector ComponentXVector = FVector::ZeroVector; // world space vector in the direction of component local X
FVector ComponentYVector = FVector::ZeroVector; // world space vector in the direction of component local Y
// Counter used to reduce how often we call compact on the map when removing sections
int32 SectionsRemovedSinceLastCompact;
uint32 LandscapeKey = 0;
FSceneInterface* Scene = nullptr;
FLandscapeRenderSystem(uint32 InLandscapeKey, FSceneInterface* InScene);
~FLandscapeRenderSystem();
static void CreateResources(FRHICommandListBase& RHICmdList, FLandscapeSectionInfo* SectionInfo);
static void DestroyResources(FLandscapeSectionInfo* SectionInfo);
static void RegisterSection(FLandscapeSectionInfo* SectionInfo);
static void UnregisterSection(FLandscapeSectionInfo* SectionInfo);
bool IsValidCoord(FIntPoint InRenderCoord) const
{
return InRenderCoord.X >= Min.X && InRenderCoord.X < Min.X + Size.X &&
InRenderCoord.Y >= Min.Y && InRenderCoord.Y < Min.Y + Size.Y;
}
int32 GetSectionLinearIndex(FIntPoint InRenderCoord) const
{
check(IsValidCoord(InRenderCoord));
int32 LinearIndex = (InRenderCoord.Y - Min.Y) * Size.X + InRenderCoord.X - Min.X;
return LinearIndex;
}
void ResizeAndMoveTo(FIntPoint NewMin, FIntPoint NewMax);
void ResizeToInclude(const FIntPoint& NewCoord);
void CompactMap();
bool AnySectionsInRangeInclusive(FIntPoint RangeMin, FIntPoint RangeMax);
void SetSectionInfo(FIntPoint InRenderCoord, FLandscapeSectionInfo* InSectionInfo)
{
if (IsValidCoord(InRenderCoord))
{
SectionInfos[GetSectionLinearIndex(InRenderCoord)] = InSectionInfo;
}
}
FLandscapeSectionInfo* GetSectionInfo(FIntPoint InRenderCoord)
{
if (IsValidCoord(InRenderCoord))
{
return SectionInfos[GetSectionLinearIndex(InRenderCoord)];
}
return nullptr;
}
float GetSectionLODValue(const FSceneView& InView, FIntPoint InRenderCoord) const;
const TResourceArray<float>& GetCachedSectionLODValues(const FSceneView& InView) const;
float GetSectionLODBias(FIntPoint InRenderCoord) const;
const TResourceArray<float>& ComputeSectionsLODForView(const FSceneView& InView, UE::Renderer::Private::IShadowInvalidatingInstances* InShadowInvalidatingInstances);
void PerformShadowInvalidations(UE::Renderer::Private::IShadowInvalidatingInstances& InShadowInvalidatingInstances);
void FetchHeightmapLODBiases();
void UpdateBuffers(FRHICommandListBase& RHICmdList);
private:
void CreateResources_Internal(FRHICommandListBase& RHICmdList, FLandscapeSectionInfo* InSectionInfo);
void DestroyResources_Internal(FLandscapeSectionInfo* InSectionInfo);
};
//
// FLandscapeLODOverridesCustomRenderPassUserData
//
class FLandscapeLODOverridesCustomRenderPassUserData : public ICustomRenderPassUserData
{
public:
IMPLEMENT_CUSTOM_RENDER_PASS_USER_DATA(FLandscapeLODOverridesCustomRenderPassUserData);
FLandscapeLODOverridesCustomRenderPassUserData(const TMap<uint32, int32>& InLandscapeLODOverrides)
: LandscapeLODOverrides(InLandscapeLODOverrides)
{}
const TMap<uint32, int32>& GetLandscapeLODOverrides() const { return LandscapeLODOverrides; }
private:
TMap<uint32, int32> LandscapeLODOverrides;
};
//
// FLandscapeSceneViewExtension
//
class FLandscapeSceneViewExtension : public FSceneViewExtensionBase
{
public:
FLandscapeSceneViewExtension(const FAutoRegister& AutoReg);
virtual ~FLandscapeSceneViewExtension();
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override;
virtual void PreInitViews_RenderThread(FRDGBuilder& GraphBuilder) override;
LANDSCAPE_API static const TMap<uint32, FLandscapeRenderSystem*>& GetLandscapeRenderSystems();
static TArray<FLandscapeRenderSystem*> GetLandscapeRenderSystems(const class FSceneInterface* InScene);
static FLandscapeRenderSystem* GetLandscapeRenderSystem(const class FSceneInterface* InScene, uint32 InLandscapeKey);
int32 GetNumViewsWithShowCollision() const { return NumViewsWithShowCollision; }
private:
void EndFrame_GameThread();
void EndFrame_RenderThread();
private:
struct FLandscapeViewData
{
FLandscapeViewData() = default;
FLandscapeViewData(FSceneView& InView);
FSceneView* View = nullptr;
// Optional interface to use if the view needs to invalidate the shadow cache :
UE::Renderer::Private::IShadowInvalidatingInstances* ShadowInvalidatingInstances = nullptr;
TResourceArray<uint32> LandscapeIndirection;
TResourceArray<float> LandscapeLODData;
};
TArray<FLandscapeViewData> LandscapeViews;
UE::Tasks::FTask LandscapeSetupTask;
int32 NumViewsWithShowCollision = 0; // Last frame number of views with collision enabled.
int32 NumViewsWithShowCollisionAcc = 0; // Accumulate the number of views with collision
};
//
// FLandscapeDebugOptions
//
struct FLandscapeDebugOptions
{
LANDSCAPE_API FLandscapeDebugOptions();
bool bShowPatches;
bool bDisableStatic;
private:
FAutoConsoleCommand PatchesConsoleCommand;
FAutoConsoleCommand StaticConsoleCommand;
void Patches();
void Static();
};
LANDSCAPE_API extern FLandscapeDebugOptions GLandscapeDebugOptions;
//
// FLandscapeMeshProxySceneProxy
//
class FLandscapeMeshProxySceneProxy final : public FStaticMeshSceneProxy
{
public:
SIZE_T GetTypeHash() const override;
FLandscapeMeshProxySceneProxy(UStaticMeshComponent* InComponent, const FGuid& InLandscapeGuid, const TArray<FIntPoint>& InProxySectionsBases, const TArray<FVector>& InProxySectionsCentersLocalSpace, const FVector& InComponentXVector, const FVector& InComponentYVector, const FTransform& LocalToWorld, int32 ComponentResolution, int8 InProxyLOD, uint32 InLODGroupKey, uint32 LandscapeKey);
virtual void CreateRenderThreadResources(FRHICommandListBase& RHICmdList) override;
virtual void DestroyRenderThreadResources() override;
protected:
virtual void OnForceHiddenChanged() override;
private:
void RegisterSections();
void UnregisterSections();
TArray<TUniquePtr<FLandscapeSectionInfo>> ProxySectionsInfos;
};
//
// FLandscapeComponentSceneProxy
//
class FLandscapeComponentSceneProxy : public FPrimitiveSceneProxy, public FLandscapeSectionInfo
{
friend class FLandscapeSharedBuffers;
LANDSCAPE_API SIZE_T GetTypeHash() const override;
class FLandscapeLCI final : public FLightCacheInterface
{
public:
/** Initialization constructor. */
FLandscapeLCI(const ULandscapeComponent* InComponent, ERHIFeatureLevel::Type FeatureLevel, bool bVFRequiresPrimitiveUniformBuffer)
: FLightCacheInterface()
{
if (InComponent->GetLightmapType() == ELightmapType::ForceVolumetric)
{
SetGlobalVolumeLightmap(true);
}
const FMeshMapBuildData* MapBuildData = InComponent->GetMeshMapBuildData();
if (MapBuildData)
{
SetLightMap(MapBuildData->LightMap);
SetShadowMap(MapBuildData->ShadowMap);
SetResourceCluster(MapBuildData->ResourceCluster);
// If landscape uses VF that requires primitive UB that means it does not use GPUScene therefore it may need precomputed lighting buffer as well
if (FeatureLevel >= ERHIFeatureLevel::SM5 && !bVFRequiresPrimitiveUniformBuffer)
{
// Landscape does not support GPUScene on mobile
// TODO: enable this when GPUScene support is implemented
bCanUsePrecomputedLightingParametersFromGPUScene = true;
}
IrrelevantLights = MapBuildData->IrrelevantLights;
}
}
// FLightCacheInterface
virtual FLightInteraction GetInteraction(const FLightSceneProxy* LightSceneProxy) const override;
private:
TArray<FGuid> IrrelevantLights;
};
public:
static const int8 MAX_SUBSECTION_COUNT = 2*2;
#if RHI_RAYTRACING
TPimplPtr<FLandscapeRayTracingImpl> RayTracingImpl;
#endif
friend FLandscapeRenderSystem;
// Reference counted vertex and index buffer shared among all landscape scene proxies of the same component size
// Key is the component size and number of subsections.
// Also being reused by GPULightmass currently to save mem
static LANDSCAPE_API TMap<uint32, FLandscapeSharedBuffers*> SharedBuffersMap;
protected:
// Maximum LOD level, user override possible
int8 MaxLOD;
int8 NumWeightmapLayerAllocations;
uint8 StaticLightingLOD;
uint8 VirtualTexturePerPixelHeight;
float WeightmapSubsectionOffset;
// Table of valid screen size -> LOD index
TArray<float> LODScreenRatioSquared;
// First LOD we have batch elements for
int32 FirstLOD;
// Last LOD we have batch elements for
int32 LastLOD;
int32 FirstVirtualTextureLOD;
int32 LastVirtualTextureLOD;
// The max extend value in any axis
float ComponentMaxExtend;
// 1.0 / LODBlendRange
float InvLODBlendRange;
FLandscapeRenderSystem::LODSettingsComponent LODSettings;
/**
* Number of subsections within the component in each dimension, this can be 1 or 2.
* Subsections exist to improve the speed at which LOD transitions can take place over distance.
*/
int32 NumSubsections;
/** Number of unique heights in the subsection. */
int32 SubsectionSizeQuads;
/** Number of heightmap heights in the subsection. This includes the duplicate row at the end. */
int32 SubsectionSizeVerts;
/** Size of the component in unique heights. */
int32 ComponentSizeQuads;
/**
* ComponentSizeQuads + 1.
* Note: in the case of multiple subsections, this is not very useful, as there will be an internal duplicate row of heights in addition to the row at the end.
*/
int32 ComponentSizeVerts;
/** Number of ray tracing sections in a landscape section for better culling and optimized BLAS builds */
int32 NumRayTracingSections;
float StaticLightingResolution;
/** Address of the component within the parent Landscape in unique height texels. */
FIntPoint SectionBase;
FMatrix LocalToWorldNoScaling;
// Storage for static draw list batch params
TArray<FLandscapeBatchElementParams> StaticBatchParamArray;
bool bNaniteActive;
bool bUsesLandscapeCulling;
// Precomputed grass rendering MeshBatch and per-LOD params
FMeshBatch GrassMeshBatch;
TArray<FLandscapeBatchElementParams> GrassBatchParams;
FVector4f WeightmapScaleBias;
TArray<UTexture2D*> WeightmapTextures;
UTexture2D* VisibilityWeightmapTexture;
int32 VisibilityWeightmapChannel;
#if WITH_EDITOR
TArray<FLinearColor> LayerColors;
static ULandscapeEditResourcesSubsystem* LandscapeEditResourcesSubsystem;
#endif
// Heightmap in RG and Normalmap in BA
UTexture2D* HeightmapTexture;
FVector4f HeightmapScaleBias;
float HeightmapSubsectionOffsetU;
float HeightmapSubsectionOffsetV;
UTexture2D* XYOffsetmapTexture;
uint32 SharedBuffersKey;
FLandscapeSharedBuffers* SharedBuffers;
FLandscapeVertexFactory* VertexFactory;
FLandscapeVertexFactory* FixedGridVertexFactory;
/** All available materials, including LOD Material, Tessellation generated materials*/
TArray<FMaterialRenderProxy*> AvailableMaterials;
// FLightCacheInterface
TUniquePtr<FLandscapeLCI> ComponentLightInfo;
/** Mapping between LOD and Material Index*/
TArray<int8> LODIndexToMaterialIndex;
/** Mapping between Material Index to Static Mesh Batch */
TArray<int8> MaterialIndexToStaticMeshBatchLOD;
/** Material Relevance for each material in AvailableMaterials */
TArray<FMaterialRelevance> MaterialRelevances;
/** Number of mips that are actually usable (that have more than 1 vertex) */
int32 NumRelevantMips = 0;
/** Maximum deltas between vertices and their counterparts from other mips (see additional details in ULandscapeComponent)
* Stored in world space to avoid useless runtime computation :
*/
TArray<double> WorldSpaceMipToMipMaxDeltas;
/** Constant bias to handle the worst artifacts of the continuous LOD morphing when rendering to VSM. */
float VirtualShadowMapConstantDepthBias;
/** Height threshold to invalidate VSM pages when using non-Nanite landscape. */
float VirtualShadowMapInvalidationHeightErrorThreshold;
#if WITH_EDITORONLY_DATA
FLandscapeEditToolRenderData EditToolRenderData;
#endif
// data used in editor or visualisers
#if WITH_EDITOR || !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
int32 CollisionMipLevel;
int32 SimpleCollisionMipLevel;
FCollisionResponseContainer CollisionResponse;
#endif
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
/** LightMap resolution used for VMI_LightmapDensity */
int32 LightMapResolution;
#endif
TUniformBuffer<FLandscapeUniformShaderParameters> LandscapeUniformShaderParameters;
// Cached versions of these
FMatrix WorldToLocal;
#if !UE_BUILD_SHIPPING
FName DebugName;
#endif // !UE_BUILD_SHIPPING
protected:
LANDSCAPE_API virtual ~FLandscapeComponentSceneProxy();
LANDSCAPE_API bool GetMeshElementForFixedGrid(int32 InLodIndex, FMaterialRenderProxy* InMaterialInterface, FMeshBatch& OutMeshBatch, TArray<FLandscapeBatchElementParams>& OutStaticBatchParamArray) const;
template<class ArrayType> bool GetStaticMeshElement(int32 LODIndex, bool bForToolMesh, FMeshBatch& MeshBatch, ArrayType& OutStaticBatchParamArray) const;
virtual void OnForceHiddenChanged() override;
public:
// constructor
LANDSCAPE_API FLandscapeComponentSceneProxy(ULandscapeComponent* InComponent);
// FPrimitiveSceneProxy interface.
LANDSCAPE_API virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override;
LANDSCAPE_API virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override;
LANDSCAPE_API virtual void ApplyViewDependentMeshArguments(const FSceneView& View, FMeshBatch& ViewDependentMeshBatch) const override;
virtual uint32 GetMemoryFootprint() const override { return(sizeof(*this) + GetAllocatedSize()); }
LANDSCAPE_API virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override;
LANDSCAPE_API virtual bool CanBeOccluded() const override;
LANDSCAPE_API virtual void GetLightRelevance(const FLightSceneProxy* LightSceneProxy, bool& bDynamic, bool& bRelevant, bool& bLightMapped, bool& bShadowMapped) const override;
LANDSCAPE_API virtual void OnTransformChanged(FRHICommandListBase& RHICmdList) override;
LANDSCAPE_API virtual void CreateRenderThreadResources(FRHICommandListBase& RHICmdList) override;
LANDSCAPE_API virtual void DestroyRenderThreadResources() override;
friend class ULandscapeComponent;
friend class FLandscapeVertexFactoryVertexShaderParameters;
friend class FLandscapeXYOffsetVertexFactoryVertexShaderParameters;
friend class FLandscapeVertexFactoryPixelShaderParameters;
friend struct FLandscapeBatchElementParams;
const FMeshBatch& GetGrassMeshBatch() const { return GrassMeshBatch; }
// FLandcapeSceneProxy
LANDSCAPE_API virtual bool HeightfieldHasPendingStreaming() const override;
LANDSCAPE_API virtual void GetHeightfieldRepresentation(UTexture2D*& OutHeightmapTexture, UTexture2D*& OutVisibilityTexture, FHeightfieldComponentDescription& OutDescription) const override;
LANDSCAPE_API virtual void GetLCIs(FLCIArray& LCIs) override;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
virtual int32 GetLightMapResolution() const override { return LightMapResolution; }
#endif
#if RHI_RAYTRACING
LANDSCAPE_API virtual void GetDynamicRayTracingInstances(FRayTracingInstanceCollector& Collector) override final;
virtual bool HasRayTracingRepresentation() const override { return true; }
virtual bool IsRayTracingRelevant() const override { return true; }
#endif
// FLandscapeSectionInfo interface
LANDSCAPE_API virtual float ComputeLODForView(const FSceneView& InView) const override;
LANDSCAPE_API virtual float ComputeLODBias() const override;
LANDSCAPE_API virtual void OnRenderCoordsChanged(FRHICommandListBase& RHICmdList) override;
LANDSCAPE_API virtual int32 GetComponentResolution() const override;
LANDSCAPE_API virtual double ComputeSectionResolution() const override;
LANDSCAPE_API virtual void GetSectionBoundsAndLocalToWorld(FBoxSphereBounds& LocalBounds, FMatrix& LocalToWorld) const override;
virtual void GetSectionCenterAndVectors(FVector& OutSectionCenterWorldSpace, FVector& OutSectionXVectorWorldSpace, FVector& OutSectionYVectorWorldSpace) const override;
virtual const FPrimitiveSceneInfo* GetPrimitiveSceneInfo() const override;
virtual bool ShouldInvalidateShadows(const FSceneView& InView, float InLODValue, float InLastShadowInvalidationLODValue) const override;
};
class FLandscapeDebugMaterialRenderProxy : public FMaterialRenderProxy
{
public:
const FMaterialRenderProxy* const Parent;
const UTexture2D* RedTexture;
const UTexture2D* GreenTexture;
const UTexture2D* BlueTexture;
const FLinearColor R;
const FLinearColor G;
const FLinearColor B;
/** Initialization constructor. */
FLandscapeDebugMaterialRenderProxy(const FMaterialRenderProxy* InParent, const UTexture2D* TexR, const UTexture2D* TexG, const UTexture2D* TexB,
const FLinearColor& InR, const FLinearColor& InG, const FLinearColor& InB) :
FMaterialRenderProxy(InParent->GetMaterialName()),
Parent(InParent),
RedTexture(TexR),
GreenTexture(TexG),
BlueTexture(TexB),
R(InR),
G(InG),
B(InB)
{}
// FMaterialRenderProxy interface.
virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetMaterialNoFallback(InFeatureLevel);
}
virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetFallback(InFeatureLevel);
}
virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
{
switch (Type)
{
case EMaterialParameterType::Vector:
if (ParameterInfo.Name == FName(TEXT("Landscape_RedMask")))
{
OutValue = R;
return true;
}
else if (ParameterInfo.Name == FName(TEXT("Landscape_GreenMask")))
{
OutValue = G;
return true;
}
else if (ParameterInfo.Name == FName(TEXT("Landscape_BlueMask")))
{
OutValue = B;
return true;
}
break;
case EMaterialParameterType::Texture:
if (ParameterInfo.Name == FName(TEXT("Landscape_RedTexture")))
{
OutValue = RedTexture;
return true;
}
else if (ParameterInfo.Name == FName(TEXT("Landscape_GreenTexture")))
{
OutValue = GreenTexture;
return true;
}
else if (ParameterInfo.Name == FName(TEXT("Landscape_BlueTexture")))
{
OutValue = BlueTexture;
return true;
}
break;
default:
break;
}
return Parent->GetParameterValue(Type, ParameterInfo, OutValue, Context);
}
};
class FLandscapeSelectMaterialRenderProxy : public FMaterialRenderProxy
{
public:
const FMaterialRenderProxy* const Parent;
const UTexture2D* SelectTexture;
/** Initialization constructor. */
FLandscapeSelectMaterialRenderProxy(const FMaterialRenderProxy* InParent, const UTexture2D* InTexture) :
FMaterialRenderProxy(InParent->GetMaterialName()),
Parent(InParent),
SelectTexture(InTexture)
{}
// FMaterialRenderProxy interface.
virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetMaterialNoFallback(InFeatureLevel);
}
virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetFallback(InFeatureLevel);
}
virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
{
switch (Type)
{
case EMaterialParameterType::Vector:
if (ParameterInfo.Name == FName(TEXT("HighlightColor")))
{
OutValue = FLinearColor(1.f, 0.5f, 0.5f);
return true;
}
break;
case EMaterialParameterType::Texture:
if (ParameterInfo.Name == FName(TEXT("SelectedData")))
{
OutValue = SelectTexture;
return true;
}
break;
default:
break;
}
return Parent->GetParameterValue(Type, ParameterInfo, OutValue, Context);
}
};
class FLandscapeMaskMaterialRenderProxy : public FMaterialRenderProxy
{
public:
const FMaterialRenderProxy* const Parent;
const UTexture2D* SelectTexture;
const bool bInverted;
/** Initialization constructor. */
FLandscapeMaskMaterialRenderProxy(const FMaterialRenderProxy* InParent, const UTexture2D* InTexture, const bool InbInverted) :
FMaterialRenderProxy(InParent->GetMaterialName()),
Parent(InParent),
SelectTexture(InTexture),
bInverted(InbInverted)
{}
// FMaterialRenderProxy interface.
virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetMaterialNoFallback(InFeatureLevel);
}
virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetFallback(InFeatureLevel);
}
virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
{
switch (Type)
{
case EMaterialParameterType::Scalar:
if (ParameterInfo.Name == FName(TEXT("bInverted")))
{
OutValue = (float)bInverted;
return true;
}
break;
case EMaterialParameterType::Texture:
if (ParameterInfo.Name == FName(TEXT("SelectedData")))
{
OutValue = SelectTexture;
return true;
}
break;
default:
break;
}
return Parent->GetParameterValue(Type, ParameterInfo, OutValue, Context);
}
};
class FLandscapeLayerUsageRenderProxy : public FMaterialRenderProxy
{
const FMaterialRenderProxy* const Parent;
int32 ComponentSizeVerts;
TArray<FLinearColor> LayerColors;
float Rotation;
public:
FLandscapeLayerUsageRenderProxy(const FMaterialRenderProxy* InParent, int32 InComponentSizeVerts, const TArray<FLinearColor>& InLayerColors, float InRotation)
: FMaterialRenderProxy(InParent->GetMaterialName())
, Parent(InParent)
, ComponentSizeVerts(InComponentSizeVerts)
, LayerColors(InLayerColors)
, Rotation(InRotation)
{}
// FMaterialRenderProxy interface.
virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetMaterialNoFallback(InFeatureLevel);
}
virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
{
return Parent->GetFallback(InFeatureLevel);
}
virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
{
static FName ColorNames[] =
{
FName(TEXT("Color0")),
FName(TEXT("Color1")),
FName(TEXT("Color2")),
FName(TEXT("Color3")),
FName(TEXT("Color4")),
FName(TEXT("Color5")),
FName(TEXT("Color6")),
FName(TEXT("Color7")),
FName(TEXT("Color8")),
FName(TEXT("Color9")),
FName(TEXT("Color10")),
FName(TEXT("Color11")),
FName(TEXT("Color12")),
FName(TEXT("Color13")),
FName(TEXT("Color14")),
FName(TEXT("Color15"))
};
switch (Type)
{
case EMaterialParameterType::Vector:
for (int32 i = 0; i < UE_ARRAY_COUNT(ColorNames) && i < LayerColors.Num(); i++)
{
if (ParameterInfo.Name == ColorNames[i])
{
OutValue = LayerColors[i];
return true;
}
}
break;
case EMaterialParameterType::Scalar:
if (ParameterInfo.Name == FName(TEXT("Rotation")))
{
OutValue = Rotation;
return true;
}
else if (ParameterInfo.Name == FName(TEXT("NumStripes")))
{
OutValue = (float)LayerColors.Num();
return true;
}
else if (ParameterInfo.Name == FName(TEXT("ComponentSizeVerts")))
{
OutValue = (float)ComponentSizeVerts;
return true;
}
break;
default:
break;
}
return Parent->GetParameterValue(Type, ParameterInfo, OutValue, Context);
}
};