638 lines
22 KiB
C++
638 lines
22 KiB
C++
|
// Copyright 2020 Phyronnaz
|
||
|
|
||
|
#include "VoxelRender/VoxelProceduralMeshSceneProxy.h"
|
||
|
#include "VoxelRender/VoxelProceduralMeshComponent.h"
|
||
|
#include "VoxelRender/VoxelProcMeshBuffers.h"
|
||
|
#include "VoxelRender/VoxelMaterialInterface.h"
|
||
|
#include "VoxelRender/VoxelToolRendering.h"
|
||
|
#include "VoxelDebug/VoxelDebugManager.h"
|
||
|
#include "VoxelMinimal.h"
|
||
|
|
||
|
#include "Engine/Engine.h"
|
||
|
#include "Materials/Material.h"
|
||
|
#include "PhysicsEngine/BodySetup.h"
|
||
|
#include "DistanceFieldAtlas.h"
|
||
|
#include "PrimitiveSceneInfo.h"
|
||
|
|
||
|
// Needed to cancel motion blur when reusing proxies
|
||
|
#include "Renderer/Private/ScenePrivate.h"
|
||
|
|
||
|
#define NOT_SHIPPING_NOR_TEST !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||
|
|
||
|
static TAutoConsoleVariable<int32> CVarLogProcMeshDelays(
|
||
|
TEXT("voxel.renderer.LogProcMeshDelays"),
|
||
|
0,
|
||
|
TEXT("If true, will log the time elapsed between the game thread update and the first render thread display"),
|
||
|
ECVF_Default);
|
||
|
|
||
|
static TAutoConsoleVariable<int32> CVarShowToolRendering(
|
||
|
TEXT("voxel.renderer.ShowToolRendering"),
|
||
|
0,
|
||
|
TEXT("If true, will show the duplicated meshes for tool rendering in red"),
|
||
|
ECVF_Default);
|
||
|
|
||
|
static TAutoConsoleVariable<int32> CVarShowMeshSections(
|
||
|
TEXT("voxel.renderer.ShowMeshSections"),
|
||
|
0,
|
||
|
TEXT("If true, will assign a unique color to each mesh section"),
|
||
|
ECVF_Default);
|
||
|
|
||
|
DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelMeshDistanceFieldMemory);
|
||
|
|
||
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Draw Calls"), STAT_NumVoxelDrawCalls, STATGROUP_VoxelCounters);
|
||
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Draw Calls For Tools"), STAT_NumVoxelDrawCallsForTools, STATGROUP_VoxelCounters);
|
||
|
|
||
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Triangles Drawn "), STAT_NumVoxelTrianglesDrawn, STATGROUP_VoxelCounters);
|
||
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Triangles Drawn For Tools"), STAT_NumVoxelTrianglesDrawnForTools, STATGROUP_VoxelCounters);
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
FVoxelProcMeshBuffersRenderData::FVoxelProcMeshBuffersRenderData(
|
||
|
const TVoxelSharedRef<const FVoxelProcMeshBuffers>& Buffers,
|
||
|
ERHIFeatureLevel::Type FeatureLevel)
|
||
|
: Buffers(Buffers)
|
||
|
, VertexFactory(FeatureLevel, "FVoxelProcMeshBuffersRenderData")
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
check(IsInRenderingThread());
|
||
|
|
||
|
{
|
||
|
auto& InitBuffers = const_cast<FVoxelProcMeshBuffers&>(*Buffers);
|
||
|
BeginInitResource(&InitBuffers.VertexBuffers.PositionVertexBuffer);
|
||
|
BeginInitResource(&InitBuffers.VertexBuffers.StaticMeshVertexBuffer);
|
||
|
BeginInitResource(&InitBuffers.VertexBuffers.ColorVertexBuffer);
|
||
|
BeginInitResource(&InitBuffers.IndexBuffer);
|
||
|
BeginInitResource(&InitBuffers.AdjacencyIndexBuffer);
|
||
|
}
|
||
|
|
||
|
auto& VertexBuffers = Buffers->VertexBuffers;
|
||
|
auto& IndexBuffer = Buffers->IndexBuffer;
|
||
|
|
||
|
FLocalVertexFactory::FDataType Data;
|
||
|
VertexBuffers.PositionVertexBuffer.BindPositionVertexBuffer(&VertexFactory, Data);
|
||
|
VertexBuffers.StaticMeshVertexBuffer.BindTangentVertexBuffer(&VertexFactory, Data);
|
||
|
VertexBuffers.StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&VertexFactory, Data);
|
||
|
VertexBuffers.ColorVertexBuffer.BindColorVertexBuffer(&VertexFactory, Data);
|
||
|
VertexFactory.SetData(Data);
|
||
|
VertexFactory.InitResource();
|
||
|
|
||
|
#if RHI_RAYTRACING
|
||
|
if (IsRayTracingEnabled())
|
||
|
{
|
||
|
FRayTracingGeometryInitializer Initializer;
|
||
|
Initializer.IndexBuffer = IndexBuffer.IndexBufferRHI;
|
||
|
Initializer.TotalPrimitiveCount = IndexBuffer.GetNumIndices() / 3;
|
||
|
Initializer.GeometryType = RTGT_Triangles;
|
||
|
Initializer.bFastBuild = true;
|
||
|
Initializer.bAllowUpdate = false;
|
||
|
|
||
|
FRayTracingGeometrySegment Segment;
|
||
|
Segment.VertexBuffer = VertexBuffers.PositionVertexBuffer.VertexBufferRHI;
|
||
|
Segment.NumPrimitives = Initializer.TotalPrimitiveCount;
|
||
|
Segment.MaxVertices = VertexBuffers.PositionVertexBuffer.GetNumVertices();
|
||
|
Initializer.Segments.Add(Segment);
|
||
|
|
||
|
RayTracingGeometry.SetInitializer(Initializer);
|
||
|
RayTracingGeometry.InitResource();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
TVoxelSharedRef<FVoxelProcMeshBuffersRenderData> FVoxelProcMeshBuffersRenderData::GetRenderData(
|
||
|
const TVoxelSharedRef<const FVoxelProcMeshBuffers>& Buffers,
|
||
|
ERHIFeatureLevel::Type FeatureLevel)
|
||
|
{
|
||
|
check(IsInRenderingThread());
|
||
|
if (!Buffers->RenderData.IsValid())
|
||
|
{
|
||
|
auto Result = TVoxelSharedRef<FVoxelProcMeshBuffersRenderData>(new FVoxelProcMeshBuffersRenderData(Buffers, FeatureLevel));
|
||
|
Buffers->RenderData = Result;
|
||
|
return Result;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return Buffers->RenderData.Pin().ToSharedRef();
|
||
|
}
|
||
|
}
|
||
|
FVoxelProcMeshBuffersRenderData::~FVoxelProcMeshBuffersRenderData()
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
check(IsInRenderingThread());
|
||
|
|
||
|
auto& InitBuffers = const_cast<FVoxelProcMeshBuffers&>(*Buffers);
|
||
|
InitBuffers.VertexBuffers.PositionVertexBuffer.ReleaseResource();
|
||
|
InitBuffers.VertexBuffers.StaticMeshVertexBuffer.ReleaseResource();
|
||
|
InitBuffers.VertexBuffers.ColorVertexBuffer.ReleaseResource();
|
||
|
InitBuffers.IndexBuffer.ReleaseResource();
|
||
|
InitBuffers.AdjacencyIndexBuffer.ReleaseResource();
|
||
|
VertexFactory.ReleaseResource();
|
||
|
|
||
|
#if RHI_RAYTRACING
|
||
|
if (IsRayTracingEnabled())
|
||
|
{
|
||
|
RayTracingGeometry.ReleaseResource();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
inline bool IsCollisionView(const FEngineShowFlags& EngineShowFlags)
|
||
|
{
|
||
|
return EngineShowFlags.CollisionVisibility || EngineShowFlags.CollisionPawn;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
FVoxelProceduralMeshSceneProxy::FVoxelProceduralMeshSceneProxy(UVoxelProceduralMeshComponent* Component)
|
||
|
: FPrimitiveSceneProxy(Component)
|
||
|
, Component(Component)
|
||
|
, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
|
||
|
, LOD(Component->LOD)
|
||
|
, DebugChunkId(Component->DebugChunkId)
|
||
|
, WeakToolRenderingManager(Component->ToolRenderingManager)
|
||
|
, CollisionResponse(Component->GetCollisionResponseToChannels())
|
||
|
, CollisionTraceFlag(Component->CollisionTraceFlag)
|
||
|
{
|
||
|
VOXEL_FUNCTION_COUNTER();
|
||
|
|
||
|
// Proxy settings
|
||
|
bSupportsDistanceFieldRepresentation = true;
|
||
|
DistanceFieldSelfShadowBias = Component->DistanceFieldSelfShadowBias;
|
||
|
bVerifyUsedMaterials = false; // Fails with tool rendering
|
||
|
|
||
|
FinishSectionsUpdatesTime = Component->LastFinishSectionsUpdatesTime;
|
||
|
CreateSceneProxyTime = FPlatformTime::Seconds();
|
||
|
|
||
|
// Copy distance field data
|
||
|
DistanceFieldData = Component->DistanceFieldData;
|
||
|
|
||
|
// Copy each section
|
||
|
const int32 NumSections = Component->ProcMeshSections.Num();
|
||
|
Sections.SetNum(NumSections);
|
||
|
for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++)
|
||
|
{
|
||
|
const auto& SrcSection = Component->ProcMeshSections[SectionIndex];
|
||
|
FVoxelProcMeshProxySection& NewSection = Sections[SectionIndex];
|
||
|
|
||
|
ensure(SrcSection.Settings.bSectionVisible || SrcSection.Settings.bEnableCollisions || SrcSection.Settings.bEnableNavmesh);
|
||
|
|
||
|
if (SrcSection.Buffers->GetNumVertices() == 0)
|
||
|
{
|
||
|
NewSection.bSectionVisible = false;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Visibility
|
||
|
NewSection.bSectionVisible = SrcSection.Settings.bSectionVisible;
|
||
|
|
||
|
// Copy debug info
|
||
|
NewSection.bEnableCollisions_Debug = SrcSection.Settings.bEnableCollisions;
|
||
|
NewSection.bEnableNavmesh_Debug = SrcSection.Settings.bEnableNavmesh;
|
||
|
|
||
|
// Grab material
|
||
|
NewSection.Material = SrcSection.Settings.Material;
|
||
|
if (!NewSection.Material.IsValid())
|
||
|
{
|
||
|
NewSection.Material = FVoxelMaterialInterfaceManager::Get().DefaultMaterial();
|
||
|
}
|
||
|
check(NewSection.Material.IsValid());
|
||
|
|
||
|
// Copy buffer ptr
|
||
|
NewSection.Buffers = SrcSection.Buffers;
|
||
|
|
||
|
// Tessellation
|
||
|
auto& IndexBuffer = NewSection.Buffers->IndexBuffer;
|
||
|
auto& AdjacencyIndexBuffer = NewSection.Buffers->AdjacencyIndexBuffer;
|
||
|
|
||
|
check(
|
||
|
AdjacencyIndexBuffer.GetNumIndices() == 0 ||
|
||
|
AdjacencyIndexBuffer.GetNumIndices() == 4 * IndexBuffer.GetNumIndices());
|
||
|
|
||
|
const bool bTessellatedMaterial = false;
|
||
|
const bool bHasAdjacency = AdjacencyIndexBuffer.GetNumIndices() > 0;
|
||
|
|
||
|
ensure(SrcSection.Settings.bEnableTessellation == bHasAdjacency);
|
||
|
ensureMsgf(bTessellatedMaterial == bHasAdjacency, TEXT("Invalid tessellated material or non tessellated material is tessellated"));
|
||
|
NewSection.bRequiresAdjacencyInformation = bTessellatedMaterial && bHasAdjacency;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// Hack to cancel motion blur when mesh components are reused in the same frame
|
||
|
const FMatrix PreviousLocalToWorld = Component->GetRenderMatrix();
|
||
|
ENQUEUE_RENDER_COMMAND(UpdateTransformCommand)(
|
||
|
[this, PreviousLocalToWorld](FRHICommandListImmediate& RHICmdList)
|
||
|
{
|
||
|
FScene& Scene = static_cast<FScene&>(GetScene());
|
||
|
Scene.VelocityData.OverridePreviousTransform(GetPrimitiveComponentId(), PreviousLocalToWorld);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FVoxelProceduralMeshSceneProxy::~FVoxelProceduralMeshSceneProxy()
|
||
|
{
|
||
|
for (auto& Section : Sections)
|
||
|
{
|
||
|
check(!Section.RenderData.IsValid());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void FVoxelProceduralMeshSceneProxy::CreateRenderThreadResources()
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
check(IsInRenderingThread());
|
||
|
|
||
|
for (auto& Section : Sections)
|
||
|
{
|
||
|
check(!Section.RenderData.IsValid());
|
||
|
check(Section.Buffers.IsValid());
|
||
|
if (Section.bSectionVisible || NOT_SHIPPING_NOR_TEST) // Need to init for debug
|
||
|
{
|
||
|
Section.RenderData = FVoxelProcMeshBuffersRenderData::GetRenderData(Section.Buffers.ToSharedRef(), GetScene().GetFeatureLevel());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FVoxelProceduralMeshSceneProxy::DestroyRenderThreadResources()
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
check(IsInRenderingThread());
|
||
|
|
||
|
for (auto& Section : Sections)
|
||
|
{
|
||
|
Section.RenderData.Reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void FVoxelProceduralMeshSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
|
||
|
const auto& EngineShowFlags = ViewFamily.EngineShowFlags;
|
||
|
|
||
|
#if NOT_SHIPPING_NOR_TEST
|
||
|
// Hack to see the delay between update call and actual mesh render update
|
||
|
if (!bLoggedTime && CVarLogProcMeshDelays.GetValueOnRenderThread() != 0 && FinishSectionsUpdatesTime > 0)
|
||
|
{
|
||
|
const double Time = FPlatformTime::Seconds();
|
||
|
LOG_VOXEL(Log, TEXT("Proc Mesh Delays: ChunkId: %u; CreateSceneProxy: %fms; GetDynamicMeshElements: %fms"),
|
||
|
DebugChunkId,
|
||
|
(CreateSceneProxyTime - FinishSectionsUpdatesTime) * 1000,
|
||
|
(Time - FinishSectionsUpdatesTime) * 1000);
|
||
|
bLoggedTime = true;
|
||
|
}
|
||
|
|
||
|
// Render bounds
|
||
|
{
|
||
|
VOXEL_SLOW_SCOPE_COUNTER("Render Bounds");
|
||
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
|
{
|
||
|
if (VisibilityMap & (1 << ViewIndex))
|
||
|
{
|
||
|
RenderBounds(Collector.GetPDI(ViewIndex), EngineShowFlags, GetBounds(), IsSelected());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsCollisionView(EngineShowFlags))
|
||
|
{
|
||
|
if (ShouldDrawComplexCollisions(EngineShowFlags))
|
||
|
{
|
||
|
VOXEL_SLOW_SCOPE_COUNTER("Complex Collisons");
|
||
|
for (auto& Section : Sections)
|
||
|
{
|
||
|
if (!Section.bEnableCollisions_Debug) continue;
|
||
|
|
||
|
const FColor ComplexCollisionColor = FColor(0, 255, 255, 255);
|
||
|
auto* MaterialProxy = new FColoredMaterialRenderProxy(GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(), ComplexCollisionColor);
|
||
|
Collector.RegisterOneFrameMaterialProxy(MaterialProxy);
|
||
|
|
||
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
|
{
|
||
|
if (!(VisibilityMap & (1 << ViewIndex))) continue;
|
||
|
|
||
|
FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, false, EngineShowFlags.Wireframe);
|
||
|
Collector.AddMesh(ViewIndex, Mesh);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (FVoxelDebugManager::ShowCollisionAndNavmeshDebug())
|
||
|
{
|
||
|
VOXEL_SLOW_SCOPE_COUNTER("Collision and Navmesh Debug");
|
||
|
for (auto& Section : Sections)
|
||
|
{
|
||
|
if (!Section.RenderData.IsValid()) continue;
|
||
|
|
||
|
const auto* ParentMaterial =
|
||
|
EngineShowFlags.Wireframe
|
||
|
? GEngine->WireframeMaterial.Get()
|
||
|
: GEngine->LevelColorationLitMaterial.Get();
|
||
|
|
||
|
const auto Color = FVoxelDebugManager::GetCollisionAndNavmeshDebugColor(Section.bEnableCollisions_Debug, Section.bEnableNavmesh_Debug);
|
||
|
|
||
|
if (!ensure(ParentMaterial)) return; // Happens in packaged games
|
||
|
|
||
|
auto* MaterialProxy = new FColoredMaterialRenderProxy(ParentMaterial->GetRenderProxy(), Color);
|
||
|
Collector.RegisterOneFrameMaterialProxy(MaterialProxy);
|
||
|
|
||
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
|
{
|
||
|
if (!(VisibilityMap & (1 << ViewIndex))) continue;
|
||
|
|
||
|
FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, false, EngineShowFlags.Wireframe);
|
||
|
Mesh.bCanApplyViewModeOverrides = false;
|
||
|
Collector.AddMesh(ViewIndex, Mesh);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
const bool bForceDisableTessellation = !(EngineShowFlags.Materials || EngineShowFlags.Wireframe); // else crash in eg lighting
|
||
|
|
||
|
for (const auto& Section : Sections)
|
||
|
{
|
||
|
VOXEL_SLOW_SCOPE_COUNTER("Render Section");
|
||
|
|
||
|
if (!Section.bSectionVisible)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
auto* Material = Section.Material->GetMaterial();
|
||
|
if (!Material)
|
||
|
{
|
||
|
// Will happen in force delete
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
auto* MaterialProxy = Material->GetRenderProxy();
|
||
|
|
||
|
if (CVarShowMeshSections.GetValueOnRenderThread() != 0)
|
||
|
{
|
||
|
uint32 Hash = 0;
|
||
|
for (auto& Guid : Section.Buffers->Guids)
|
||
|
{
|
||
|
Hash = FVoxelUtilities::MurmurHash64((uint64(Hash) << 32) ^ Guid.A ^ Guid.B ^ Guid.C ^ Guid.D);
|
||
|
}
|
||
|
MaterialProxy = new FColoredMaterialRenderProxy(GEngine->LevelColorationLitMaterial->GetRenderProxy(), reinterpret_cast<FColor&>(Hash));
|
||
|
Collector.RegisterOneFrameMaterialProxy(MaterialProxy);
|
||
|
}
|
||
|
|
||
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
|
{
|
||
|
if (!(VisibilityMap & (1 << ViewIndex))) continue;
|
||
|
|
||
|
FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, !bForceDisableTessellation, EngineShowFlags.Wireframe);
|
||
|
|
||
|
{
|
||
|
VOXEL_SLOW_SCOPE_COUNTER("Collector.AddMesh");
|
||
|
Collector.AddMesh(ViewIndex, Mesh);
|
||
|
}
|
||
|
|
||
|
INC_DWORD_STAT(STAT_NumVoxelDrawCalls);
|
||
|
INC_DWORD_STAT_BY(STAT_NumVoxelTrianglesDrawn, Section.Buffers->GetNumIndices() / 3);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const auto ToolRenderingManager = WeakToolRenderingManager.Pin();
|
||
|
if (ToolRenderingManager.IsValid())
|
||
|
{
|
||
|
VOXEL_RENDER_SCOPE_COUNTER("Render Tools");
|
||
|
|
||
|
TArray<FVoxelToolRendering, TInlineAllocator<64>> Tools;
|
||
|
|
||
|
const FBox WorldBounds = GetBounds().GetBox();
|
||
|
ToolRenderingManager->IterateTools(
|
||
|
[&](const FVoxelToolRendering& Tool)
|
||
|
{
|
||
|
if (Tool.bEnabled && Tool.WorldBounds.Intersect(WorldBounds))
|
||
|
{
|
||
|
Tools.Add(Tool);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
for (auto& Tool : Tools)
|
||
|
{
|
||
|
UMaterialInterface* Material = nullptr;
|
||
|
if (Tool.Material.IsValid())
|
||
|
{
|
||
|
Material = Tool.Material->GetMaterial();
|
||
|
}
|
||
|
if (!Material)
|
||
|
{
|
||
|
Material = FVoxelMaterialInterfaceManager::Get().DefaultMaterial()->GetMaterial();
|
||
|
}
|
||
|
if (!ensure(Material))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
auto* MaterialProxy = Material->GetRenderProxy();
|
||
|
|
||
|
// Hack to fix translucent rendering when the tool material was changed but the mesh wasn't updated
|
||
|
const_cast<FMaterialRelevance&>(MaterialRelevance) |= Material->GetMaterial()->GetRelevance_Concurrent(GetScene().GetFeatureLevel());
|
||
|
|
||
|
if (CVarShowToolRendering.GetValueOnRenderThread() != 0)
|
||
|
{
|
||
|
MaterialProxy = new FColoredMaterialRenderProxy(GEngine->LevelColorationUnlitMaterial->GetRenderProxy(), FColor::Red);
|
||
|
Collector.RegisterOneFrameMaterialProxy(MaterialProxy);
|
||
|
}
|
||
|
|
||
|
for (const auto& Section : Sections)
|
||
|
{
|
||
|
if (!Section.bSectionVisible) continue;
|
||
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
|
{
|
||
|
if (!(VisibilityMap & (1 << ViewIndex))) continue;
|
||
|
|
||
|
FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, !bForceDisableTessellation, EngineShowFlags.Wireframe);
|
||
|
Collector.AddMesh(ViewIndex, Mesh);
|
||
|
|
||
|
INC_DWORD_STAT(STAT_NumVoxelDrawCallsForTools);
|
||
|
INC_DWORD_STAT_BY(STAT_NumVoxelTrianglesDrawnForTools, Section.Buffers->GetNumIndices() / 3)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#if RHI_RAYTRACING
|
||
|
void FVoxelProceduralMeshSceneProxy::GetDynamicRayTracingInstances(FRayTracingMaterialGatheringContext& Context, TArray<FRayTracingInstance>& OutRayTracingInstances)
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
|
||
|
for (const auto& Section : Sections)
|
||
|
{
|
||
|
if (Section.bSectionVisible && ensure(Section.Material->GetMaterial()->IsValidLowLevel()))
|
||
|
{
|
||
|
auto& RenderData = *Section.RenderData;
|
||
|
if (RenderData.RayTracingGeometry.RayTracingGeometryRHI.IsValid())
|
||
|
{
|
||
|
check(RenderData.RayTracingGeometry.Initializer.IndexBuffer.IsValid());
|
||
|
|
||
|
FRayTracingInstance RayTracingInstance;
|
||
|
RayTracingInstance.Geometry = &RenderData.RayTracingGeometry;
|
||
|
RayTracingInstance.InstanceTransforms.Add(GetLocalToWorld());
|
||
|
|
||
|
FMeshBatch MeshBatch;
|
||
|
|
||
|
MeshBatch.VertexFactory = &RenderData.VertexFactory;
|
||
|
MeshBatch.SegmentIndex = 0;
|
||
|
MeshBatch.MaterialRenderProxy = Section.Material->GetMaterial()->GetRenderProxy();
|
||
|
MeshBatch.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
||
|
MeshBatch.Type = PT_TriangleList;
|
||
|
MeshBatch.DepthPriorityGroup = SDPG_World;
|
||
|
MeshBatch.bCanApplyViewModeOverrides = false;
|
||
|
|
||
|
FMeshBatchElement& BatchElement = MeshBatch.Elements[0];
|
||
|
BatchElement.IndexBuffer = &Section.Buffers->IndexBuffer;
|
||
|
|
||
|
BatchElement.FirstIndex = 0;
|
||
|
BatchElement.NumPrimitives = Section.Buffers->IndexBuffer.GetNumIndices() / 3;
|
||
|
BatchElement.MinVertexIndex = 0;
|
||
|
BatchElement.MaxVertexIndex = Section.Buffers->VertexBuffers.PositionVertexBuffer.GetNumVertices() - 1;
|
||
|
|
||
|
RayTracingInstance.Materials.Add(MeshBatch);
|
||
|
|
||
|
RayTracingInstance.BuildInstanceMaskAndFlags(GetScene().GetFeatureLevel());
|
||
|
OutRayTracingInstances.Add(RayTracingInstance);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
FPrimitiveViewRelevance FVoxelProceduralMeshSceneProxy::GetViewRelevance(const FSceneView* View) const
|
||
|
{
|
||
|
FPrimitiveViewRelevance Result;
|
||
|
Result.bDrawRelevance = IsShown(View);
|
||
|
Result.bShadowRelevance = IsShadowCast(View);
|
||
|
Result.bDynamicRelevance = true;
|
||
|
Result.bRenderInMainPass = ShouldRenderInMainPass();
|
||
|
Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
|
||
|
Result.bRenderCustomDepth = ShouldRenderCustomDepth();
|
||
|
Result.bTranslucentSelfShadow = bCastVolumetricTranslucentShadow;
|
||
|
MaterialRelevance.SetPrimitiveViewRelevance(Result);
|
||
|
Result.bVelocityRelevance = IsMovable() && Result.bOpaque && Result.bRenderInMainPass;
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
bool FVoxelProceduralMeshSceneProxy::CanBeOccluded() const
|
||
|
{
|
||
|
return !MaterialRelevance.bDisableDepthTest;
|
||
|
}
|
||
|
|
||
|
uint32 FVoxelProceduralMeshSceneProxy::GetMemoryFootprint() const
|
||
|
{
|
||
|
return sizeof(*this) + GetAllocatedSize();
|
||
|
}
|
||
|
|
||
|
SIZE_T FVoxelProceduralMeshSceneProxy::GetTypeHash() const
|
||
|
{
|
||
|
static size_t UniquePointer;
|
||
|
return reinterpret_cast<size_t>(&UniquePointer);
|
||
|
}
|
||
|
|
||
|
uint32 FVoxelProceduralMeshSceneProxy::GetAllocatedSize() const
|
||
|
{
|
||
|
return Sections.GetAllocatedSize() + FPrimitiveSceneProxy::GetAllocatedSize();
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
FMeshBatch& FVoxelProceduralMeshSceneProxy::DrawSection(
|
||
|
FMeshElementCollector& Collector,
|
||
|
const FVoxelProcMeshProxySection& Section,
|
||
|
const FMaterialRenderProxy* MaterialRenderProxy,
|
||
|
bool bEnableTessellation,
|
||
|
bool bWireframe) const
|
||
|
{
|
||
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
||
|
|
||
|
check(MaterialRenderProxy);
|
||
|
check(Section.RenderData.IsValid());
|
||
|
|
||
|
FMeshBatch& Mesh = Collector.AllocateMesh();
|
||
|
|
||
|
Mesh.VertexFactory = &Section.RenderData->VertexFactory;
|
||
|
Mesh.MaterialRenderProxy = MaterialRenderProxy;
|
||
|
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
||
|
Mesh.Type = PT_TriangleList;
|
||
|
Mesh.DepthPriorityGroup = SDPG_World;
|
||
|
Mesh.bUseWireframeSelectionColoring = IsSelected() && bWireframe; // Else mesh LODs view is messed up when actor is selected
|
||
|
Mesh.bCanApplyViewModeOverrides = true;
|
||
|
#if NOT_SHIPPING_NOR_TEST
|
||
|
Mesh.VisualizeLODIndex = LOD % GEngine->LODColorationColors.Num();
|
||
|
#endif
|
||
|
|
||
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
||
|
BatchElement.IndexBuffer = &Section.Buffers->IndexBuffer;
|
||
|
BatchElement.FirstIndex = 0;
|
||
|
BatchElement.NumPrimitives = Section.Buffers->IndexBuffer.GetNumIndices() / 3;
|
||
|
BatchElement.MinVertexIndex = 0;
|
||
|
BatchElement.MaxVertexIndex = Section.Buffers->VertexBuffers.PositionVertexBuffer.GetNumVertices() - 1;
|
||
|
|
||
|
#if ENABLE_TESSELLATION
|
||
|
if (bEnableTessellation)
|
||
|
{
|
||
|
// Could be different from bRequiresAdjacencyInformation during shader compilation
|
||
|
const bool bCurrentRequiresAdjacencyInformation = false;
|
||
|
|
||
|
if (ensure(Section.Buffers->IndexBuffer.GetNumIndices() != 0) &&
|
||
|
Section.bRequiresAdjacencyInformation &&
|
||
|
bCurrentRequiresAdjacencyInformation)
|
||
|
{
|
||
|
Mesh.Type = PT_12_ControlPointPatchList;
|
||
|
BatchElement.IndexBuffer = &Section.Buffers->AdjacencyIndexBuffer;
|
||
|
BatchElement.FirstIndex *= 4;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return Mesh;
|
||
|
}
|
||
|
|
||
|
bool FVoxelProceduralMeshSceneProxy::ShouldDrawComplexCollisions(const FEngineShowFlags& EngineShowFlags) const
|
||
|
{
|
||
|
if (IsCollisionEnabled())
|
||
|
{
|
||
|
// See if we have a response to the interested channel
|
||
|
bool bHasResponse = EngineShowFlags.CollisionPawn && CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore;
|
||
|
bHasResponse |= EngineShowFlags.CollisionVisibility && CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore;
|
||
|
|
||
|
if (bHasResponse)
|
||
|
{
|
||
|
// Visibility uses complex and pawn uses simple. However, if UseSimpleAsComplex or UseComplexAsSimple is used we need to adjust accordingly
|
||
|
return
|
||
|
(EngineShowFlags.CollisionVisibility && CollisionTraceFlag != ECollisionTraceFlag::CTF_UseSimpleAsComplex) ||
|
||
|
(EngineShowFlags.CollisionPawn && CollisionTraceFlag == ECollisionTraceFlag::CTF_UseComplexAsSimple);
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|