// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Materials/MaterialInterface.h" #include "PrimitiveSceneProxy.h" #include "Materials/MaterialRelevance.h" #include "WaterQuadTree.h" #include "WaterQuadTreeBuilder.h" #include "WaterVertexFactory.h" #include "RayTracingGeometry.h" #include "RenderGraphResources.h" #include "WaterQuadTreeGPU.h" class FMeshElementCollector; class UWaterMeshComponent; using FWaterInstanceDataBuffersType = TWaterInstanceDataBuffers; using FWaterMeshUserDataBuffersType = TWaterMeshUserDataBuffers; using FWaterMeshUserDataType = TWaterMeshUserData; /** Set of quadtree related constants that do not change over the lifetime of the FWaterMeshSceneProxy and are shared by all quadtrees owned by it. */ struct FWaterQuadTreeConstants { /** Scale of the concentric LOD squares */ float LODScale = -1.0f; /** Number of quads per side of a water quad tree tile at LOD0 */ int32 NumQuadsLOD0 = 0; int32 NumQuadsPerIndirectDrawTile = 0; /** Number of densities (same as number of grid index/vertex buffers) */ int32 DensityCount = 0; int32 ForceCollapseDensityLevel = TNumericLimits::Max(); }; /** A water quadtree instance owned by FWaterMeshSceneProxy and associated with a certain view. In splitscreen, each player should get their own FViewWaterQuadTree. */ class FViewWaterQuadTree { public: struct FWaterLODParams { int32 LowestLOD; float HeightLODFactor; float WaterHeightForLOD; }; struct FUserDataAndIndirectArgs { TStaticArray UserData = {}; TRefCountPtr IndirectArgs = nullptr; }; // Rebuilds the quadtree at the specified position. void Update(const FWaterQuadTreeBuilder& Builder, const FVector2D& CenterPosition); // Traverses the GPU quadtree to build indirect draw calls and associated buffers. May also initialize the GPU quadtree if it wasn't initialized already. void TraverseGPUQuadTree(FRDGBuilder& GraphBuilder, bool bDepthBufferIsPopulated); // Allocates transient GPU resources to build indirect draw calls for the GPU quadtree and returns parameters needed by the water vertex factory to draw the quadtree. FUserDataAndIndirectArgs PrepareGPUQuadTreeForRendering(const TArray& Views, uint32 VisibilityMap, FMeshElementCollector& Collector, const FWaterQuadTreeConstants& QuadTreeConstants, const TArrayView & BatchRenderGroups, FRHICommandListBase& RHICmdList) const; // Evaluates the CPU quadtree at the given position and returns parameters used for CPU quadtree draw call generation. FWaterLODParams GetWaterLODParams(const FVector& Position, float LODScale) const; const FWaterQuadTree& GetWaterQuadTree() const { return WaterQuadTree; } FWaterInstanceDataBuffersType* GetWaterInstanceDataBuffers() const { return WaterInstanceDataBuffers.Get(); } FWaterMeshUserDataBuffersType* GetWaterMeshUserDataBuffers() const { return WaterMeshUserDataBuffers.Get(); } double GetMinHeight() const { return WaterQuadTreeMinHeight; } double GetMaxHeight() const { return WaterQuadTreeMaxHeight; } private: /** Tiles containing water, stored in a quad tree. */ FWaterQuadTree WaterQuadTree; /** GPU quad tree instance. Only initialized and used if WaterQuadTree.IsGPUQuadTree() is true. */ FWaterQuadTreeGPU QuadTreeGPU; /** Unique Instance data buffer shared accross water batch draw calls */ TUniquePtr WaterInstanceDataBuffers = nullptr; /** Per-"water render group" user data (the number of groups might vary depending on whether we're in the editor or not) */ TUniquePtr WaterMeshUserDataBuffers = nullptr; /** Vertical extent of the quadtree. */ double WaterQuadTreeMinHeight = DBL_MAX; double WaterQuadTreeMaxHeight = -DBL_MAX; mutable FWaterQuadTreeGPU::FTraverseParams WaterQuadTreeGPUTraverseParams; mutable bool bNeedToTraverseGPUQuadTree = false; /** Initializes the GPU quad tree. */ void BuildGPUQuadTree(FRDGBuilder& GraphBuilder); }; /** Water mesh scene proxy */ class FWaterMeshSceneProxy final : public FPrimitiveSceneProxy { public: SIZE_T GetTypeHash() const override { static size_t UniquePointer; return reinterpret_cast(&UniquePointer); } FWaterMeshSceneProxy(UWaterMeshComponent* Component); virtual ~FWaterMeshSceneProxy(); virtual void CreateRenderThreadResources(FRHICommandListBase& RHICmdList) override; virtual void DestroyRenderThreadResources() override; virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override; virtual const TArray* GetOcclusionQueries(const FSceneView* View) const override; virtual void AcceptOcclusionResults(const FSceneView* View, TArray* Results, int32 ResultsStart, int32 NumResults) override; virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override; virtual bool HasSubprimitiveOcclusionQueries() const override; virtual bool CanBeOccluded() const override { return !MaterialRelevance.bDisableDepthTest; } virtual uint32 GetMemoryFootprint() const override { return(sizeof(*this) + GetAllocatedSize()); } uint32 GetAllocatedSize() const { return(FPrimitiveSceneProxy::GetAllocatedSize() + (WaterVertexFactories.GetAllocatedSize() + WaterVertexFactories.Num() * sizeof(FWaterVertexFactoryType)) + ViewQuadTrees.GetAllocatedSize()); } #if WITH_WATER_SELECTION_SUPPORT virtual HHitProxy* CreateHitProxies(UPrimitiveComponent* Component, TArray >& OutHitProxies) override; #endif // WITH_WATER_SELECTION_SUPPORT // At runtime, we only ever need one version of the vertex factory : with selection support (editor) or without : using FWaterVertexFactoryType = TWaterVertexFactory; using FWaterVertexFactoryIndirectDrawType = TWaterVertexFactory; using FWaterVertexFactoryIndirectDrawISRType = TWaterVertexFactory; #if RHI_RAYTRACING virtual void GetDynamicRayTracingInstances(FRayTracingInstanceCollector& Collector) override final; virtual bool HasRayTracingRepresentation() const override { return true; } virtual bool IsRayTracingRelevant() const override { return true; } #endif // Creates and initializes a new quadtree centered around CenterPosition and associated with the given key. Returns true if the quadtree was created and false if it already exists. bool CreateViewWaterQuadTree(int32 Key, const FVector2D& CenterPosition); // Updates an existing quadtree by reconstructing it at a new CenterPosition. Returns true if the quadtree exists and was updated and false otherwise. bool UpdateViewWaterQuadTree(int32 Key, const FVector2D& CenterPosition); // Destroys the quadtree associated with Key. void DestroyViewWaterQuadTree(int32 Key); int32 FindBestQuadTreeForViewLocation(const FVector2D& ViewPosition2D) const; private: #if RHI_RAYTRACING struct FRayTracingWaterData { FRayTracingGeometry Geometry; FRWBuffer DynamicVertexBuffer; }; #endif struct FOcclusionCullingResults { uint32 FrameNumber; TArray Results; }; TMap ViewQuadTrees; FMaterialRelevance MaterialRelevance; // One vertex factory per LOD. Only used for CPU driven water quadtree rendering. TArray WaterVertexFactories; FWaterVertexFactoryIndirectDrawType* WaterVertexFactoryIndirectDraw = nullptr; FWaterVertexFactoryIndirectDrawISRType* WaterVertexFactoryIndirectDrawISR = nullptr; FWaterQuadTreeBuilder WaterQuadTreeBuilder; FWaterQuadTreeConstants WaterQuadTreeConstants; // If this is true, then this proxy can manage multiple local quadtrees, potentially associated with different views. bool bIsLocalOnlyTessellationEnabled = false; mutable int32 HistoricalMaxViewInstanceCount = 0; #if RHI_RAYTRACING // Per density array of ray tracing geometries. TArray> RayTracingWaterData; #endif // CPU-driven occlusion culling related members TArray OcclusionCullingBounds; TArray EmptyOcclusionCullingBounds; TMap OcclusionResults; UE::FMutex OcclusionResultsMutex; int32 OcclusionResultsFarMeshOffset = INT32_MAX; uint32 SceneProxyCreatedFrameNumberRenderThread = INDEX_NONE; bool HasWaterData() const; TArray> GetBatchRenderGroups(const TArray& Views, uint32 VisibilityMap) const; uint32 GetWireframeVisibilityMapAndMaterial(const TArray& Views, uint32 VisibilityMap, FMeshElementCollector& Collector, class FColoredMaterialRenderProxy*& OutMaterialInstance) const; int32 FindBestQuadTreeForView(const FSceneView* View) const; TArray> GetViewToQuadTreeMapping(const TArray& Views, uint32 VisibilityMap) const; #if RHI_RAYTRACING void SetupRayTracingInstances(FRHICommandListBase& RHICmdList, int32 NumInstances, uint32 DensityIndex); #endif };