1691 lines
62 KiB
C++
1691 lines
62 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "GeometryCollection/GeometryCollectionSceneProxy.h"
|
|
|
|
#include "Async/ParallelFor.h"
|
|
#include "Engine/Engine.h"
|
|
#include "GeometryCollection/GeometryCollection.h"
|
|
#include "GeometryCollection/GeometryCollectionObject.h"
|
|
#include "MaterialDomain.h"
|
|
#include "MaterialShaderType.h"
|
|
#include "MaterialShared.h"
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
#include "CommonRenderResources.h"
|
|
#include "Rendering/NaniteResources.h"
|
|
#include "PrimitiveSceneInfo.h"
|
|
#include "GeometryCollection/GeometryCollectionComponent.h"
|
|
#include "GeometryCollection/GeometryCollectionAlgo.h"
|
|
#include "GeometryCollection/GeometryCollectionHitProxy.h"
|
|
#include "GeometryCollection/GeometryCollectionDebugDraw.h"
|
|
#include "RHIDefinitions.h"
|
|
#include "ComponentReregisterContext.h"
|
|
#include "ComponentRecreateRenderStateContext.h"
|
|
#include "RenderGraphBuilder.h"
|
|
#include "MeshPaintVisualize.h"
|
|
#include "SceneView.h"
|
|
|
|
#if RHI_RAYTRACING
|
|
#include "RayTracingInstance.h"
|
|
#endif
|
|
|
|
#if INTEL_ISPC
|
|
#if USING_CODE_ANALYSIS
|
|
MSVC_PRAGMA( warning( push ) )
|
|
MSVC_PRAGMA( warning( disable : ALL_CODE_ANALYSIS_WARNINGS ) )
|
|
#endif // USING_CODE_ANALYSIS
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wnonportable-include-path"
|
|
#endif
|
|
|
|
#include "GeometryCollectionSceneProxy.ispc.generated.h"
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#if USING_CODE_ANALYSIS
|
|
MSVC_PRAGMA( warning( pop ) )
|
|
#endif // USING_CODE_ANALYSIS
|
|
|
|
static_assert(sizeof(ispc::FMatrix44f) == sizeof(FMatrix44f), "sizeof(ispc::FMatrix44f) != sizeof(FMatrix44f)");
|
|
static_assert(sizeof(ispc::FVector3f) == sizeof(FVector3f), "sizeof(ispc::FVector3f) != sizeof(FVector3f)");
|
|
#endif
|
|
|
|
static int32 GParallelGeometryCollectionBatchSize = 1024;
|
|
static TAutoConsoleVariable<int32> CVarParallelGeometryCollectionBatchSize(
|
|
TEXT("r.ParallelGeometryCollectionBatchSize"),
|
|
GParallelGeometryCollectionBatchSize,
|
|
TEXT("The number of vertices per thread dispatch in a single collection. \n"),
|
|
ECVF_Default
|
|
);
|
|
|
|
static int32 GGeometryCollectionTripleBufferUploads = 1;
|
|
FAutoConsoleVariableRef CVarGeometryCollectionTripleBufferUploads(
|
|
TEXT("r.GeometryCollectionTripleBufferUploads"),
|
|
GGeometryCollectionTripleBufferUploads,
|
|
TEXT("Whether to triple buffer geometry collection uploads, which allows Lock_NoOverwrite uploads which are much faster on the GPU with large amounts of data."),
|
|
ECVF_Default
|
|
);
|
|
|
|
static int32 GRayTracingGeometryCollection = 0;
|
|
FAutoConsoleVariableRef CVarRayTracingGeometryCollection(
|
|
TEXT("r.RayTracing.Geometry.GeometryCollection"),
|
|
GRayTracingGeometryCollection,
|
|
TEXT("Include geometry collection proxy meshes in ray tracing effects (default = 0 (Geometry collection meshes disabled in ray tracing))"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static bool GRayTracingGeometryCollectionWPO = true;
|
|
static FAutoConsoleVariableRef CVarRayTracingGeometryCollectionWPO(
|
|
TEXT("r.RayTracing.Geometry.GeometryCollection.WPO"),
|
|
GRayTracingGeometryCollectionWPO,
|
|
TEXT("Whether to update geometry collection ray tracing representation based on material World Position Offset."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRayTracingGeometryCollectionForceUpdate(
|
|
TEXT("r.RayTracing.Geometry.GeometryCollection.ForceUpdate"),
|
|
0,
|
|
TEXT("Forces ray tracing representation for geometry collections meshes to be updated every frame."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRayTracingGeometryCollectionCombinedBLAS(
|
|
TEXT("r.RayTracing.Geometry.GeometryCollection.CombinedBLAS"),
|
|
0,
|
|
TEXT("Whether to always use a combined BLAS instead of one instance per collection part.\n")
|
|
TEXT("A combined BLAS needs to be fully rebuilt whenever any transform changes.\n")
|
|
TEXT("This is automatically enabled for geometry collections using WPO since BLAS must be updated anyway."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
|
|
#if !defined(CHAOS_GEOMETRY_COLLECTION_SET_DYNAMIC_DATA_ISPC_ENABLED_DEFAULT)
|
|
#define CHAOS_GEOMETRY_COLLECTION_SET_DYNAMIC_DATA_ISPC_ENABLED_DEFAULT 1
|
|
#endif
|
|
|
|
// Support run-time toggling on supported platforms in non-shipping configurations
|
|
#if !INTEL_ISPC || UE_BUILD_SHIPPING
|
|
static constexpr bool bGeometryCollection_SetDynamicData_ISPC_Enabled = INTEL_ISPC && CHAOS_GEOMETRY_COLLECTION_SET_DYNAMIC_DATA_ISPC_ENABLED_DEFAULT;
|
|
#else
|
|
static bool bGeometryCollection_SetDynamicData_ISPC_Enabled = CHAOS_GEOMETRY_COLLECTION_SET_DYNAMIC_DATA_ISPC_ENABLED_DEFAULT;
|
|
static FAutoConsoleVariableRef CVarGeometryCollectionSetDynamicDataISPCEnabled(TEXT("r.GeometryCollectionSetDynamicData.ISPC"), bGeometryCollection_SetDynamicData_ISPC_Enabled, TEXT("Whether to use ISPC optimizations to set dynamic data in geometry collections"));
|
|
#endif
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(FGeometryCollectionSceneProxyLogging, Log, All);
|
|
|
|
FGeometryCollectionDynamicDataPool GDynamicDataPool;
|
|
|
|
static void UpdateLooseParameter(
|
|
FGeometryCollectionVertexFactory& VertexFactory,
|
|
FRHIShaderResourceView* BoneTransformSRV,
|
|
FRHIShaderResourceView* BonePrevTransformSRV,
|
|
FRHIShaderResourceView* BoneMapSRV)
|
|
{
|
|
FGCBoneLooseParameters LooseParameters;
|
|
|
|
LooseParameters.VertexFetch_BoneTransformBuffer = BoneTransformSRV;
|
|
LooseParameters.VertexFetch_BonePrevTransformBuffer = BonePrevTransformSRV;
|
|
LooseParameters.VertexFetch_BoneMapBuffer = BoneMapSRV;
|
|
|
|
EUniformBufferUsage UniformBufferUsage = VertexFactory.EnableLooseParameter ? UniformBuffer_SingleFrame : UniformBuffer_MultiFrame;
|
|
|
|
VertexFactory.LooseParameterUniformBuffer = FGCBoneLooseParametersRef::CreateUniformBufferImmediate(LooseParameters, UniformBufferUsage);
|
|
}
|
|
|
|
|
|
class FGeometryCollectionMeshCollectorResources : public FOneFrameResource
|
|
{
|
|
public:
|
|
FGeometryCollectionVertexFactory VertexFactory;
|
|
|
|
FGeometryCollectionMeshCollectorResources(ERHIFeatureLevel::Type InFeatureLevel)
|
|
: VertexFactory(InFeatureLevel,true)
|
|
{
|
|
}
|
|
|
|
virtual ~FGeometryCollectionMeshCollectorResources()
|
|
{
|
|
VertexFactory.ReleaseResource();
|
|
}
|
|
|
|
virtual FGeometryCollectionVertexFactory& GetVertexFactory() { return VertexFactory; }
|
|
};
|
|
|
|
|
|
FGeometryCollectionSceneProxyBase::FGeometryCollectionSceneProxyBase(UGeometryCollectionComponent* Component, bool bInIsNanite)
|
|
: bIsNanite(bInIsNanite)
|
|
, FeatureLevel(Component->GetScene()->GetFeatureLevel())
|
|
, MeshResource(Component->GetRestCollection()->RenderData->MeshResource)
|
|
, MaterialRelevance(Component->GetMaterialRelevance(FeatureLevel))
|
|
, VertexFactory(FeatureLevel)
|
|
, bUseShaderBoneTransform(VertexFactory.UseShaderBoneTransform(Component->GetScene()->GetShaderPlatform()))
|
|
, bSupportsTripleBufferVertexUpload(GRHISupportsMapWriteNoOverwrite)
|
|
#if RHI_RAYTRACING
|
|
, bHasRayTracingRepresentation(IsRayTracingEnabled() && Component->GetRestCollection()->RenderData->bHasMeshData && Component->GetRestCollection()->RenderData->MeshDescription.NumVertices != 0)
|
|
#endif
|
|
{
|
|
if (!bIsNanite || bHasRayTracingRepresentation)
|
|
{
|
|
MeshDescription = Component->GetRestCollection()->RenderData->MeshDescription; // TODO: use const-reference instead?
|
|
|
|
const TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> Collection = Component->GetRestCollection()->GetGeometryCollection();
|
|
|
|
NumTransforms = Collection ? Collection->NumElements(FTransformCollection::TransformGroup) : 0;
|
|
|
|
Materials.Empty();
|
|
const int32 NumMaterials = Component->GetNumMaterials();
|
|
for (int MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
|
|
{
|
|
Materials.Push(Component->GetMaterial(MaterialIndex));
|
|
|
|
if (Materials[MaterialIndex] == nullptr || !Materials[MaterialIndex]->CheckMaterialUsage_Concurrent(MATUSAGE_GeometryCollections))
|
|
{
|
|
Materials[MaterialIndex] = UMaterial::GetDefaultMaterial(MD_Surface);
|
|
}
|
|
}
|
|
}
|
|
|
|
DynamicData = Component->InitDynamicData(true);
|
|
}
|
|
|
|
FGeometryCollectionSceneProxyBase::~FGeometryCollectionSceneProxyBase()
|
|
{
|
|
if (DynamicData != nullptr)
|
|
{
|
|
GDynamicDataPool.Release(DynamicData);
|
|
DynamicData = nullptr;
|
|
}
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxyBase::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
|
|
{
|
|
if (!bIsNanite || bHasRayTracingRepresentation)
|
|
{
|
|
if (bUseShaderBoneTransform)
|
|
{
|
|
// Initialize transform buffers and upload rest transforms.
|
|
TransformBuffers.AddDefaulted(1);
|
|
|
|
TransformBuffers[0].NumTransforms = NumTransforms;
|
|
TransformBuffers[0].InitResource(RHICmdList);
|
|
|
|
const bool bLocalGeometryCollectionTripleBufferUploads = (GGeometryCollectionTripleBufferUploads != 0) && bSupportsTripleBufferVertexUpload;
|
|
const EResourceLockMode LockMode = bLocalGeometryCollectionTripleBufferUploads ? RLM_WriteOnly_NoOverwrite : RLM_WriteOnly;
|
|
|
|
FGeometryCollectionTransformBuffer& TransformBuffer = GetCurrentTransformBuffer();
|
|
TransformBuffer.UpdateDynamicData(RHICmdList, DynamicData->Transforms, LockMode);
|
|
}
|
|
else
|
|
{
|
|
// Initialize CPU skinning buffer with rest transforms.
|
|
SkinnedPositionVertexBuffer.Init(MeshResource.PositionVertexBuffer.GetNumVertices(), false);
|
|
SkinnedPositionVertexBuffer.InitResource(RHICmdList);
|
|
UpdateSkinnedPositions(RHICmdList, DynamicData->Transforms);
|
|
}
|
|
|
|
SetupVertexFactory(RHICmdList, VertexFactory);
|
|
}
|
|
|
|
bRenderResourcesCreated = true;
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxyBase::DestroyRenderThreadResources()
|
|
{
|
|
bRenderResourcesCreated = false;
|
|
|
|
if (!bIsNanite || bHasRayTracingRepresentation)
|
|
{
|
|
if (bUseShaderBoneTransform)
|
|
{
|
|
for (int32 i = 0; i < TransformBuffers.Num(); i++)
|
|
{
|
|
TransformBuffers[i].ReleaseResource();
|
|
}
|
|
TransformBuffers.Reset();
|
|
}
|
|
else
|
|
{
|
|
SkinnedPositionVertexBuffer.ReleaseResource();
|
|
}
|
|
}
|
|
|
|
VertexFactory.ReleaseResource();
|
|
|
|
#if RHI_RAYTRACING
|
|
if (bHasRayTracingRepresentation)
|
|
{
|
|
for (FRayTracingGeometry& PartRayTracingGeometry : PartRayTracingGeometries)
|
|
{
|
|
PartRayTracingGeometry.ReleaseResource();
|
|
}
|
|
PartRayTracingGeometries.Empty();
|
|
|
|
RayTracingGeometry.ReleaseResource();
|
|
RayTracingDynamicVertexBuffer.Release();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxyBase::SetupVertexFactory(FRHICommandListBase& RHICmdList, FGeometryCollectionVertexFactory& GeometryCollectionVertexFactory, FColorVertexBuffer* ColorOverride) const
|
|
{
|
|
checkf(GeometryCollectionVertexFactory.SupportsManualVertexFetch(FeatureLevel) == VertexFactory.SupportsManualVertexFetch(FeatureLevel), TEXT("Setting up vertex factory for manual vertex fetch but provided type doesn't support it."));
|
|
|
|
FGeometryCollectionVertexFactory::FDataType Data;
|
|
|
|
FPositionVertexBuffer const& PositionVB = bUseShaderBoneTransform ? MeshResource.PositionVertexBuffer : SkinnedPositionVertexBuffer;
|
|
PositionVB.BindPositionVertexBuffer(&GeometryCollectionVertexFactory, Data);
|
|
|
|
MeshResource.StaticMeshVertexBuffer.BindTangentVertexBuffer(&GeometryCollectionVertexFactory, Data);
|
|
MeshResource.StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&GeometryCollectionVertexFactory, Data);
|
|
MeshResource.StaticMeshVertexBuffer.BindLightMapVertexBuffer(&GeometryCollectionVertexFactory, Data, 0);
|
|
|
|
FColorVertexBuffer const& ColorVB = ColorOverride ? *ColorOverride : MeshResource.ColorVertexBuffer;
|
|
ColorVB.BindColorVertexBuffer(&GeometryCollectionVertexFactory, Data);
|
|
|
|
if (bUseShaderBoneTransform)
|
|
{
|
|
Data.BoneMapSRV = MeshResource.BoneMapVertexBuffer.GetSRV();
|
|
Data.BoneTransformSRV = GetCurrentTransformBuffer().VertexBufferSRV;
|
|
Data.BonePrevTransformSRV = GetCurrentPrevTransformBuffer().VertexBufferSRV;
|
|
}
|
|
else
|
|
{
|
|
// Make sure these are not null to pass UB validation
|
|
Data.BoneMapSRV = GNullColorVertexBuffer.VertexBufferSRV;
|
|
Data.BoneTransformSRV = GNullColorVertexBuffer.VertexBufferSRV;
|
|
Data.BonePrevTransformSRV = GNullColorVertexBuffer.VertexBufferSRV;
|
|
}
|
|
|
|
GeometryCollectionVertexFactory.SetData(RHICmdList, Data);
|
|
|
|
if (!GeometryCollectionVertexFactory.IsInitialized())
|
|
{
|
|
GeometryCollectionVertexFactory.InitResource(RHICmdList);
|
|
}
|
|
else
|
|
{
|
|
GeometryCollectionVertexFactory.UpdateRHI(RHICmdList);
|
|
}
|
|
}
|
|
|
|
/** Called on render thread to setup dynamic geometry for rendering */
|
|
void FGeometryCollectionSceneProxyBase::SetDynamicData_RenderThread(FRHICommandListBase& RHICmdList, FGeometryCollectionDynamicData* NewDynamicData)
|
|
{
|
|
if (NewDynamicData != DynamicData)
|
|
{
|
|
if (DynamicData)
|
|
{
|
|
GDynamicDataPool.Release(DynamicData);
|
|
DynamicData = nullptr;
|
|
}
|
|
DynamicData = NewDynamicData;
|
|
}
|
|
|
|
if (MeshDescription.NumVertices == 0 || !DynamicData || !bRenderResourcesCreated)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!bIsNanite || bHasRayTracingRepresentation)
|
|
{
|
|
if (bUseShaderBoneTransform)
|
|
{
|
|
const bool bLocalGeometryCollectionTripleBufferUploads = (GGeometryCollectionTripleBufferUploads != 0) && bSupportsTripleBufferVertexUpload;
|
|
|
|
if (bLocalGeometryCollectionTripleBufferUploads && TransformBuffers.Num() == 1)
|
|
{
|
|
TransformBuffers.AddDefaulted(2);
|
|
check(TransformBuffers.Num() == 3);
|
|
|
|
for (int32 i = 1; i < TransformBuffers.Num(); i++)
|
|
{
|
|
TransformBuffers[i].NumTransforms = NumTransforms;
|
|
TransformBuffers[i].InitResource(RHICmdList);
|
|
}
|
|
}
|
|
|
|
// Copy the transform data over to the vertex buffer
|
|
{
|
|
const EResourceLockMode LockMode = bLocalGeometryCollectionTripleBufferUploads ? RLM_WriteOnly_NoOverwrite : RLM_WriteOnly;
|
|
|
|
CycleTransformBuffers(bLocalGeometryCollectionTripleBufferUploads);
|
|
|
|
FGeometryCollectionTransformBuffer& TransformBuffer = GetCurrentTransformBuffer();
|
|
const FGeometryCollectionTransformBuffer& PrevTransformBuffer = GetCurrentPrevTransformBuffer();
|
|
|
|
VertexFactory.SetBoneTransformSRV(TransformBuffer.VertexBufferSRV);
|
|
VertexFactory.SetBonePrevTransformSRV(PrevTransformBuffer.VertexBufferSRV);
|
|
|
|
TransformBuffer.UpdateDynamicData(RHICmdList, DynamicData->Transforms, LockMode);
|
|
|
|
UpdateLooseParameter(VertexFactory, TransformBuffer.VertexBufferSRV, PrevTransformBuffer.VertexBufferSRV, MeshResource.BoneMapVertexBuffer.GetSRV());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateSkinnedPositions(RHICmdList, DynamicData->Transforms);
|
|
}
|
|
}
|
|
|
|
#if RHI_RAYTRACING
|
|
if (RayTracingGeometry.IsInitialized())
|
|
{
|
|
RayTracingGeometry.SetRequiresBuild(true);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxyBase::UpdateSkinnedPositions(FRHICommandListBase& RHICmdList, TArray<FMatrix44f> const& Transforms)
|
|
{
|
|
const int32 VertexStride = SkinnedPositionVertexBuffer.GetStride();
|
|
const int32 VertexCount = SkinnedPositionVertexBuffer.GetNumVertices();
|
|
check(VertexCount == MeshDescription.NumVertices)
|
|
|
|
void* VertexBufferData = RHICmdList.LockBuffer(SkinnedPositionVertexBuffer.VertexBufferRHI, 0, VertexCount * VertexStride, RLM_WriteOnly);
|
|
check(VertexBufferData != nullptr);
|
|
|
|
FPositionVertexBuffer const& SourcePositionVertexBuffer = MeshResource.PositionVertexBuffer;
|
|
FBoneMapVertexBuffer const& SourceBoneMapVertexBuffer = MeshResource.BoneMapVertexBuffer;
|
|
|
|
bool bParallelGeometryCollection = true;
|
|
int32 ParallelGeometryCollectionBatchSize = CVarParallelGeometryCollectionBatchSize.GetValueOnRenderThread();
|
|
|
|
int32 NumBatches = (VertexCount / ParallelGeometryCollectionBatchSize);
|
|
|
|
if (VertexCount != ParallelGeometryCollectionBatchSize)
|
|
{
|
|
NumBatches++;
|
|
}
|
|
|
|
// Batch too small, don't bother with parallel
|
|
if (ParallelGeometryCollectionBatchSize > VertexCount)
|
|
{
|
|
bParallelGeometryCollection = false;
|
|
ParallelGeometryCollectionBatchSize = VertexCount;
|
|
}
|
|
|
|
auto GeometryCollectionBatch([&](int32 BatchNum)
|
|
{
|
|
uint32 IndexOffset = ParallelGeometryCollectionBatchSize * BatchNum;
|
|
uint32 ThisBatchSize = ParallelGeometryCollectionBatchSize;
|
|
|
|
// Check for final batch
|
|
if (IndexOffset + ParallelGeometryCollectionBatchSize > MeshDescription.NumVertices)
|
|
{
|
|
ThisBatchSize = VertexCount - IndexOffset;
|
|
}
|
|
|
|
if (ThisBatchSize > 0)
|
|
{
|
|
const FMatrix44f* RESTRICT BoneTransformsPtr = Transforms.GetData();
|
|
|
|
if (bGeometryCollection_SetDynamicData_ISPC_Enabled)
|
|
{
|
|
#if INTEL_ISPC
|
|
uint8* VertexBufferOffset = (uint8*)VertexBufferData + (IndexOffset * VertexStride);
|
|
ispc::SetDynamicData_RenderThread(
|
|
(ispc::FVector3f*)VertexBufferOffset,
|
|
ThisBatchSize,
|
|
VertexStride,
|
|
&SourceBoneMapVertexBuffer.BoneIndex(IndexOffset),
|
|
(ispc::FMatrix44f*)BoneTransformsPtr,
|
|
(ispc::FVector3f*)&SourcePositionVertexBuffer.VertexPosition(IndexOffset));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
for (uint32 i = IndexOffset; i < IndexOffset + ThisBatchSize; i++)
|
|
{
|
|
FVector3f Transformed = BoneTransformsPtr[SourceBoneMapVertexBuffer.BoneIndex(i)].TransformPosition(SourcePositionVertexBuffer.VertexPosition(i));
|
|
FMemory::Memcpy((uint8*)VertexBufferData + (i * VertexStride), &Transformed, sizeof(FVector3f));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
ParallelFor(NumBatches, GeometryCollectionBatch, !bParallelGeometryCollection);
|
|
|
|
RHICmdList.UnlockBuffer(SkinnedPositionVertexBuffer.VertexBufferRHI);
|
|
}
|
|
|
|
#if RHI_RAYTRACING
|
|
void FGeometryCollectionSceneProxyBase::UpdatingRayTracingGeometry_RenderingThread(FRHICommandListBase& RHICmdList, TConstArrayView<FGeometryCollectionMeshElement> InSectionArray, bool bRayTracingGeometryPerSection)
|
|
{
|
|
// TODO: Could use SectionsNoInternal when geometry collection is undamaged?
|
|
|
|
if (bRayTracingGeometryPerSection)
|
|
{
|
|
// release combined geometry since will use part geometries
|
|
RayTracingGeometry.ReleaseResource();
|
|
|
|
// TODO: Combine sections using the same transform into a single BLAS
|
|
|
|
if (PartRayTracingGeometries.IsEmpty())
|
|
{
|
|
PartRayTracingGeometries.SetNum(InSectionArray.Num());
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < InSectionArray.Num(); ++SectionIndex)
|
|
{
|
|
FRayTracingGeometryInitializer Initializer;
|
|
Initializer.DebugName = FDebugName(FName(TEXT("GeometryCollectionPart")), SectionIndex);
|
|
Initializer.GeometryType = RTGT_Triangles;
|
|
Initializer.bFastBuild = true;
|
|
Initializer.bAllowUpdate = false;
|
|
Initializer.TotalPrimitiveCount = 0;
|
|
Initializer.IndexBuffer = MeshResource.IndexBuffer.IndexBufferRHI;
|
|
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = InSectionArray[SectionIndex];
|
|
|
|
FRayTracingGeometrySegment Segment;
|
|
Segment.FirstPrimitive = Section.TriangleStart;
|
|
Segment.VertexBuffer = MeshResource.PositionVertexBuffer.VertexBufferRHI;
|
|
Segment.NumPrimitives = Section.TriangleCount;
|
|
Segment.MaxVertices = Section.VertexEnd;
|
|
|
|
Initializer.Segments.Add(Segment);
|
|
|
|
Initializer.TotalPrimitiveCount += Section.TriangleCount;
|
|
}
|
|
|
|
PartRayTracingGeometries[SectionIndex].SetInitializer(MoveTemp(Initializer));
|
|
PartRayTracingGeometries[SectionIndex].InitResource(RHICmdList);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// release part geometries since will use combined geometry
|
|
for (FRayTracingGeometry& PartRayTracingGeometry : PartRayTracingGeometries)
|
|
{
|
|
PartRayTracingGeometry.ReleaseResource();
|
|
}
|
|
PartRayTracingGeometries.Empty();
|
|
|
|
// initialize combined geometry if necessary
|
|
if (!RayTracingGeometry.IsInitialized())
|
|
{
|
|
FRayTracingGeometryInitializer Initializer;
|
|
Initializer.DebugName = FName(TEXT("GeometryCollection"));
|
|
Initializer.GeometryType = RTGT_Triangles;
|
|
Initializer.bFastBuild = true;
|
|
Initializer.bAllowUpdate = false;
|
|
Initializer.TotalPrimitiveCount = 0;
|
|
Initializer.IndexBuffer = MeshResource.IndexBuffer.IndexBufferRHI;
|
|
|
|
RayTracingGeometry.SetInitializer(Initializer);
|
|
|
|
// InitResource before initializing segments to avoid requesting an unnecessary build
|
|
RayTracingGeometry.InitResource(RHICmdList);
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < InSectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = InSectionArray[SectionIndex];
|
|
|
|
FRayTracingGeometrySegment Segment;
|
|
Segment.FirstPrimitive = Section.TriangleStart;
|
|
Segment.VertexBuffer = MeshResource.PositionVertexBuffer.VertexBufferRHI;
|
|
Segment.NumPrimitives = Section.TriangleCount;
|
|
Segment.MaxVertices = Section.VertexEnd;
|
|
|
|
Initializer.Segments.Add(Segment);
|
|
|
|
Initializer.TotalPrimitiveCount += Section.TriangleCount;
|
|
}
|
|
|
|
RayTracingGeometry.SetInitializer(MoveTemp(Initializer));
|
|
|
|
// Build will be requested later using dynamic geometry update path
|
|
RayTracingGeometry.CreateRayTracingGeometry(RHICmdList, ERTAccelerationStructureBuildPriority::Skip);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxyBase::GetDynamicRayTracingInstances(FRayTracingInstanceCollector& Collector, const FMatrix& LocalToWorld, FRHIUniformBuffer* UniformBuffer, bool bAnyMaterialHasWorldPositionOffset)
|
|
{
|
|
checkf(bHasRayTracingRepresentation, TEXT("Shouldn't try to get ray tracing instances from proxy that doesn't have a ray tracing representation."));
|
|
|
|
if (GRayTracingGeometryCollection == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (MeshDescription.Sections.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_GeometryCollectionSceneProxyBase_GetDynamicRayTracingInstances);
|
|
|
|
if (!GRayTracingGeometryCollectionWPO)
|
|
{
|
|
bAnyMaterialHasWorldPositionOffset = false;
|
|
}
|
|
|
|
const uint32 LODIndex = 0;
|
|
const bool bWireframe = false;
|
|
|
|
//Loose parameter needs to be updated every frame
|
|
FGeometryCollectionMeshCollectorResources* CollectorResources;
|
|
CollectorResources = &Collector.AllocateOneFrameResource<FGeometryCollectionMeshCollectorResources>(FeatureLevel);
|
|
FGeometryCollectionVertexFactory& GeometryCollectionVertexFactory = CollectorResources->GetVertexFactory();
|
|
|
|
SetupVertexFactory(Collector.GetRHICommandList(), GeometryCollectionVertexFactory);
|
|
|
|
const bool bUseSubSections = MeshDescription.SubSections.Num() && !bAnyMaterialHasWorldPositionOffset && CVarRayTracingGeometryCollectionCombinedBLAS.GetValueOnRenderThread() == 0;
|
|
|
|
TArray<FGeometryCollectionMeshElement> const& SectionArray = bUseSubSections ? MeshDescription.SubSections : MeshDescription.Sections;
|
|
|
|
UpdatingRayTracingGeometry_RenderingThread(Collector.GetRHICommandList(), SectionArray, bUseSubSections);
|
|
|
|
// Grab the material proxies we'll be using for each section
|
|
TArray<FMaterialRenderProxy*, TInlineAllocator<32>> MaterialProxies;
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
|
|
//TODO: Add BoneColor support in Path/Ray tracing?
|
|
FMaterialRenderProxy* MaterialProxy = Materials[Section.MaterialIndex]->GetRenderProxy();
|
|
|
|
if (MaterialProxy == nullptr)
|
|
{
|
|
MaterialProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
}
|
|
|
|
MaterialProxies.Add(MaterialProxy);
|
|
}
|
|
|
|
if (RayTracingGeometry.IsValid())
|
|
{
|
|
// Render dynamic objects
|
|
if (!GeometryCollectionVertexFactory.GetType()->SupportsRayTracingDynamicGeometry())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FRayTracingInstance RayTracingInstance;
|
|
RayTracingInstance.Geometry = &RayTracingGeometry;
|
|
RayTracingInstance.InstanceTransforms.Emplace(LocalToWorld);
|
|
|
|
uint32 MaxVertexIndex = 0;
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
|
|
FMeshBatch& Mesh = RayTracingInstance.Materials.AddDefaulted_GetRef();
|
|
Mesh.bWireframe = bWireframe;
|
|
Mesh.SegmentIndex = SectionIndex;
|
|
Mesh.VertexFactory = &GeometryCollectionVertexFactory;
|
|
Mesh.MaterialRenderProxy = MaterialProxies[SectionIndex];
|
|
Mesh.LODIndex = LODIndex;
|
|
Mesh.bDisableBackfaceCulling = true;
|
|
Mesh.Type = PT_TriangleList;
|
|
Mesh.DepthPriorityGroup = SDPG_World;
|
|
Mesh.bCanApplyViewModeOverrides = true;
|
|
|
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
|
BatchElement.IndexBuffer = &MeshResource.IndexBuffer;
|
|
BatchElement.PrimitiveUniformBuffer = UniformBuffer;
|
|
BatchElement.FirstIndex = Section.TriangleStart * 3;
|
|
BatchElement.NumPrimitives = Section.TriangleCount;
|
|
BatchElement.MinVertexIndex = Section.VertexStart;
|
|
BatchElement.MaxVertexIndex = Section.VertexEnd;
|
|
BatchElement.NumInstances = 1;
|
|
|
|
MaxVertexIndex = std::max(Section.VertexEnd, MaxVertexIndex);
|
|
|
|
//#TODO: bone color, bone selection and render bound?
|
|
}
|
|
|
|
const bool bAlwaysUpdate = bAnyMaterialHasWorldPositionOffset || (CVarRayTracingGeometryCollectionForceUpdate.GetValueOnRenderThread() != 0);
|
|
const bool bNeedsUpdate = bAlwaysUpdate
|
|
|| (!bAlwaysUpdate && RayTracingGeometry.DynamicGeometrySharedBufferGenerationID != FRayTracingGeometry::NonSharedVertexBuffers) // was using shared VB but won't use it anymore so update once
|
|
|| RayTracingGeometry.IsEvicted()
|
|
|| RayTracingGeometry.GetRequiresBuild();
|
|
|
|
FRWBuffer* VertexBuffer = &RayTracingDynamicVertexBuffer;
|
|
|
|
if (bAlwaysUpdate)
|
|
{
|
|
// if updating every frame release memory and use shared VB
|
|
VertexBuffer->Release();
|
|
VertexBuffer = nullptr;
|
|
}
|
|
|
|
if (bNeedsUpdate)
|
|
{
|
|
const uint32 VertexCount = MaxVertexIndex + 1;
|
|
Collector.AddRayTracingGeometryUpdate(
|
|
FRayTracingDynamicGeometryUpdateParams
|
|
{
|
|
RayTracingInstance.Materials,
|
|
false,
|
|
VertexCount,
|
|
VertexCount * (uint32)sizeof(FVector3f),
|
|
RayTracingGeometry.Initializer.TotalPrimitiveCount,
|
|
&RayTracingGeometry,
|
|
VertexBuffer,
|
|
true
|
|
}
|
|
);
|
|
}
|
|
|
|
Collector.AddRayTracingInstance(MoveTemp(RayTracingInstance));
|
|
}
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < PartRayTracingGeometries.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
|
|
FRayTracingInstance RayTracingInstance;
|
|
RayTracingInstance.Geometry = &PartRayTracingGeometries[SectionIndex];
|
|
RayTracingInstance.InstanceTransforms.Emplace(FMatrix(DynamicData->Transforms[Section.TransformIndex]) * LocalToWorld);
|
|
|
|
{
|
|
FMeshBatch& Mesh = RayTracingInstance.Materials.AddDefaulted_GetRef();
|
|
Mesh.bWireframe = bWireframe;
|
|
Mesh.SegmentIndex = 0;
|
|
Mesh.VertexFactory = &GeometryCollectionVertexFactory;
|
|
Mesh.MaterialRenderProxy = MaterialProxies[SectionIndex];
|
|
Mesh.LODIndex = LODIndex;
|
|
Mesh.bDisableBackfaceCulling = true;
|
|
Mesh.Type = PT_TriangleList;
|
|
Mesh.DepthPriorityGroup = SDPG_World;
|
|
Mesh.bCanApplyViewModeOverrides = true;
|
|
|
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
|
BatchElement.IndexBuffer = &MeshResource.IndexBuffer;
|
|
BatchElement.PrimitiveUniformBuffer = UniformBuffer;
|
|
BatchElement.FirstIndex = Section.TriangleStart * 3;
|
|
BatchElement.NumPrimitives = Section.TriangleCount;
|
|
BatchElement.MinVertexIndex = Section.VertexStart;
|
|
BatchElement.MaxVertexIndex = Section.VertexEnd;
|
|
BatchElement.NumInstances = 1;
|
|
|
|
//#TODO: bone color, bone selection and render bound?
|
|
}
|
|
|
|
Collector.AddRayTracingInstance(MoveTemp(RayTracingInstance));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint32 FGeometryCollectionSceneProxyBase::GetAllocatedSize() const
|
|
{
|
|
#if RHI_RAYTRACING
|
|
uint32 RayTracingGeometryAllocatedSize = RayTracingGeometry.RawData.GetAllocatedSize();
|
|
|
|
for (const FRayTracingGeometry& PartRayTracingGeometry : PartRayTracingGeometries)
|
|
{
|
|
RayTracingGeometryAllocatedSize += PartRayTracingGeometry.RawData.GetAllocatedSize();
|
|
}
|
|
#endif
|
|
|
|
return Materials.GetAllocatedSize()
|
|
+ MeshDescription.Sections.GetAllocatedSize()
|
|
+ MeshDescription.SubSections.GetAllocatedSize()
|
|
+ (SkinnedPositionVertexBuffer.GetAllowCPUAccess() ? SkinnedPositionVertexBuffer.GetStride() * SkinnedPositionVertexBuffer.GetNumVertices() : 0)
|
|
#if RHI_RAYTRACING
|
|
+ RayTracingGeometryAllocatedSize
|
|
#endif
|
|
;
|
|
}
|
|
|
|
FGeometryCollectionSceneProxy::FGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component)
|
|
: FPrimitiveSceneProxy(Component)
|
|
, FGeometryCollectionSceneProxyBase(Component, false)
|
|
#if WITH_EDITOR
|
|
, bShowBoneColors(Component->GetShowBoneColors())
|
|
, bSuppressSelectionMaterial(Component->GetSuppressSelectionMaterial())
|
|
, VertexFactoryDebugColor(GetScene().GetFeatureLevel())
|
|
#endif
|
|
{
|
|
if (Component->GetRestCollection())
|
|
{
|
|
GeometryCollection = Component->GetRestCollection()->GetGeometryCollection();
|
|
}
|
|
|
|
EnableGPUSceneSupportFlags();
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
// Render by SubSection if we are in the rigid body picker.
|
|
bUsesSubSections = Component->GetIsTransformSelectionMode() && MeshDescription.SubSections.Num();
|
|
// Enable bone hit selection proxies if we are in the rigid body picker or in the fracture modes.
|
|
bEnableBoneSelection = Component->GetEnableBoneSelection();
|
|
|
|
if (bEnableBoneSelection || bUsesSubSections)
|
|
{
|
|
for (int32 TransformIndex = 0; TransformIndex < NumTransforms; ++TransformIndex)
|
|
{
|
|
HGeometryCollection* HitProxy = new HGeometryCollection(Component, TransformIndex);
|
|
HitProxies.Add(HitProxy);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
if (bShowBoneColors || bEnableBoneSelection)
|
|
{
|
|
Component->GetBoneColors(BoneColors);
|
|
ColorVertexBuffer.InitFromColorArray(BoneColors);
|
|
|
|
if (Component->GetRestCollection())
|
|
{
|
|
BoneSelectedMaterial = Component->GetRestCollection()->GetBoneSelectedMaterial();
|
|
}
|
|
if (BoneSelectedMaterial && !BoneSelectedMaterial->CheckMaterialUsage_Concurrent(MATUSAGE_GeometryCollections))
|
|
{
|
|
// If we have an invalid BoneSelectedMaterial, switch it back to null to skip its usage in GetDynamicMeshElements below
|
|
BoneSelectedMaterial = nullptr;
|
|
}
|
|
|
|
// Make sure the vertex color material has the usage flag for rendering geometry collections
|
|
if (GEngine->VertexColorMaterial)
|
|
{
|
|
GEngine->VertexColorMaterial->CheckMaterialUsage_Concurrent(MATUSAGE_GeometryCollections);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// #todo(dmp): This flag means that when motion blur is turned on, it will always render geometry collections into the
|
|
// velocity buffer. Note that the way around this is to loop through the global matrices and test whether they have
|
|
// changed from the prev to curr frame, but this is expensive. We should revisit this if the draw calls for velocity
|
|
// rendering become a problem. One solution could be to use internal solver sleeping state to drive motion blur.
|
|
bAlwaysHasVelocity = true;
|
|
|
|
SetWireframeColor(Component->GetWireframeColorForSceneProxy());
|
|
CollisionResponse = Component->GetCollisionResponseToChannels();
|
|
}
|
|
|
|
FGeometryCollectionSceneProxy::~FGeometryCollectionSceneProxy()
|
|
{
|
|
}
|
|
|
|
SIZE_T FGeometryCollectionSceneProxy::GetTypeHash() const
|
|
{
|
|
static size_t UniquePointer;
|
|
return reinterpret_cast<size_t>(&UniquePointer);
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
|
|
{
|
|
FGeometryCollectionSceneProxyBase::CreateRenderThreadResources(RHICmdList);
|
|
|
|
#if WITH_EDITOR
|
|
if (bShowBoneColors || bEnableBoneSelection)
|
|
{
|
|
// Initialize debug color buffer and associated vertex factory.
|
|
ColorVertexBuffer.InitResource(RHICmdList);
|
|
SetupVertexFactory(RHICmdList, VertexFactoryDebugColor, &ColorVertexBuffer);
|
|
}
|
|
#endif
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
if (MeshDescription.NumVertices && HitProxies.Num())
|
|
{
|
|
// Create buffer containing per vertex hit proxy IDs.
|
|
HitProxyIdBuffer.Init(MeshDescription.NumVertices);
|
|
HitProxyIdBuffer.InitResource(RHICmdList);
|
|
|
|
uint16 const* BoneMapData = &MeshResource.BoneMapVertexBuffer.BoneIndex(0);
|
|
ParallelFor(MeshDescription.NumVertices, [&](int32 i)
|
|
{
|
|
// Note that some fracture undo/redo operations can: recreate scene proxy, then update render data, then recreate proxy again.
|
|
// In that case we can come here the first time with too few hit proxy objects for the bone map which hasn't updated.
|
|
// But we then enter here a second time with the render data correct.
|
|
int16 ProxyIndex = BoneMapData[i];
|
|
ProxyIndex = HitProxies.IsValidIndex(ProxyIndex) ? ProxyIndex : 0;
|
|
HitProxyIdBuffer.VertexColor(i) = HitProxies[ProxyIndex]->Id.GetColor();
|
|
});
|
|
|
|
void* VertexBufferData = RHICmdList.LockBuffer(HitProxyIdBuffer.VertexBufferRHI, 0, HitProxyIdBuffer.GetNumVertices() * HitProxyIdBuffer.GetStride(), RLM_WriteOnly);
|
|
FMemory::Memcpy(VertexBufferData, HitProxyIdBuffer.GetVertexData(), HitProxyIdBuffer.GetNumVertices() * HitProxyIdBuffer.GetStride());
|
|
RHICmdList.UnlockBuffer(HitProxyIdBuffer.VertexBufferRHI);
|
|
}
|
|
#endif
|
|
|
|
bRenderResourcesCreated = true;
|
|
SetDynamicData_RenderThread(RHICmdList, DynamicData);
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::DestroyRenderThreadResources()
|
|
{
|
|
FGeometryCollectionSceneProxyBase::DestroyRenderThreadResources();
|
|
|
|
#if WITH_EDITOR
|
|
VertexFactoryDebugColor.ReleaseResource();
|
|
ColorVertexBuffer.ReleaseResource();
|
|
#endif
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
HitProxyIdBuffer.ReleaseResource();
|
|
#endif
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::SetDynamicData_RenderThread(FRHICommandListBase& RHICmdList, FGeometryCollectionDynamicData* NewDynamicData)
|
|
{
|
|
FGeometryCollectionSceneProxyBase::SetDynamicData_RenderThread(RHICmdList, NewDynamicData);
|
|
|
|
if (MeshDescription.NumVertices == 0 || !DynamicData || !bRenderResourcesCreated)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bUseShaderBoneTransform)
|
|
{
|
|
const FGeometryCollectionTransformBuffer& TransformBuffer = GetCurrentTransformBuffer();
|
|
const FGeometryCollectionTransformBuffer& PrevTransformBuffer = GetCurrentPrevTransformBuffer();
|
|
|
|
#if WITH_EDITOR
|
|
if (bShowBoneColors || bEnableBoneSelection)
|
|
{
|
|
VertexFactoryDebugColor.SetBoneTransformSRV(TransformBuffer.VertexBufferSRV);
|
|
VertexFactoryDebugColor.SetBonePrevTransformSRV(PrevTransformBuffer.VertexBufferSRV);
|
|
UpdateLooseParameter(VertexFactoryDebugColor, TransformBuffer.VertexBufferSRV, PrevTransformBuffer.VertexBufferSRV, MeshResource.BoneMapVertexBuffer.GetSRV());
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
FMaterialRenderProxy* FGeometryCollectionSceneProxy::GetMaterial(FMeshElementCollector& Collector, int32 MaterialIndex) const
|
|
{
|
|
FMaterialRenderProxy* MaterialProxy = nullptr;
|
|
|
|
#if WITH_EDITOR
|
|
if (bShowBoneColors && GEngine->VertexColorMaterial)
|
|
{
|
|
// Material for colored bones
|
|
UMaterial* VertexColorVisualizationMaterial = GEngine->VertexColorMaterial;
|
|
FMaterialRenderProxy* VertexColorVisualizationMaterialInstance = new FColoredMaterialRenderProxy(
|
|
VertexColorVisualizationMaterial->GetRenderProxy(),
|
|
GetSelectionColor(FLinearColor::White, false, false)
|
|
);
|
|
Collector.RegisterOneFrameMaterialProxy(VertexColorVisualizationMaterialInstance);
|
|
MaterialProxy = VertexColorVisualizationMaterialInstance;
|
|
}
|
|
else
|
|
#endif
|
|
if(Materials.IsValidIndex(MaterialIndex))
|
|
{
|
|
MaterialProxy = Materials[MaterialIndex]->GetRenderProxy();
|
|
}
|
|
|
|
if (MaterialProxy == nullptr)
|
|
{
|
|
MaterialProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
}
|
|
|
|
return MaterialProxy;
|
|
}
|
|
|
|
FVertexFactory const* FGeometryCollectionSceneProxy::GetVertexFactory() const
|
|
{
|
|
#if WITH_EDITOR
|
|
return bShowBoneColors ? &VertexFactoryDebugColor : &VertexFactory;
|
|
#else
|
|
return &VertexFactory;
|
|
#endif
|
|
}
|
|
|
|
bool FGeometryCollectionSceneProxy::ShowCollisionMeshes(const FEngineShowFlags& EngineShowFlags) const
|
|
{
|
|
if (IsCollisionEnabled())
|
|
{
|
|
if (EngineShowFlags.CollisionPawn && CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore)
|
|
{
|
|
return true;
|
|
}
|
|
if (EngineShowFlags.CollisionVisibility && CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore)
|
|
{
|
|
return true;
|
|
}
|
|
if (EngineShowFlags.Collision)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_GeometryCollectionSceneProxy_GetDynamicMeshElements);
|
|
if (MeshDescription.NumVertices == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FEngineShowFlags& EngineShowFlags = ViewFamily.EngineShowFlags;
|
|
const bool bWireframe = AllowDebugViewmodes() && EngineShowFlags.Wireframe;
|
|
const bool bProxyIsSelected = IsSelected();
|
|
const bool bDrawOnlyCollisionMeshes = EngineShowFlags.CollisionPawn || EngineShowFlags.CollisionVisibility;
|
|
const bool bDrawWireframeCollision = EngineShowFlags.Collision && IsCollisionEnabled();
|
|
|
|
auto SetDebugMaterial = [this, &Collector, &EngineShowFlags, bProxyIsSelected](FMeshBatch& Mesh) -> void
|
|
{
|
|
#if UE_ENABLE_DEBUG_DRAWING
|
|
|
|
// flag to indicate whether we've set a debug material yet
|
|
// Note: Will be used if we add more debug material options
|
|
// (compare to variable of same name in StaticMeshSceneProxy.cpp)
|
|
bool bDebugMaterialRenderProxySet = false;
|
|
|
|
if (!bDebugMaterialRenderProxySet && bProxyIsSelected && EngineShowFlags.VertexColors && AllowDebugViewmodes())
|
|
{
|
|
// Note: static mesh renderer does something more complicated involving per-section selection, but whole component selection seems ok for now.
|
|
if (FMaterialRenderProxy* VertexColorVisualizationMaterialInstance = MeshPaintVisualize::GetMaterialRenderProxy(bProxyIsSelected, IsHovered()))
|
|
{
|
|
Collector.RegisterOneFrameMaterialProxy(VertexColorVisualizationMaterialInstance);
|
|
Mesh.MaterialRenderProxy = VertexColorVisualizationMaterialInstance;
|
|
bDebugMaterialRenderProxySet = true;
|
|
}
|
|
}
|
|
#endif
|
|
};
|
|
|
|
const bool bDrawGeometryCollectionMesh = !bDrawOnlyCollisionMeshes;
|
|
|
|
if (bDrawGeometryCollectionMesh)
|
|
{
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
if ((VisibilityMap & (1 << ViewIndex)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If hiding geometry in editor then we don't remove hidden faces.
|
|
const bool bRemoveInternalFaces = false;
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
// If using subsections then use the subsection array.
|
|
TArray<FGeometryCollectionMeshElement> const& SectionArray = bUsesSubSections
|
|
? MeshDescription.SubSections
|
|
: bRemoveInternalFaces ? MeshDescription.SectionsNoInternal : MeshDescription.Sections;
|
|
#else
|
|
TArray<FGeometryCollectionMeshElement> const& SectionArray = bRemoveInternalFaces ? MeshDescription.SectionsNoInternal : MeshDescription.Sections;
|
|
#endif
|
|
|
|
// Grab the material proxies we'll be using for each section.
|
|
TArray<FMaterialRenderProxy*, TInlineAllocator<32>> MaterialProxies;
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
FMaterialRenderProxy* MaterialProxy = GetMaterial(Collector, Section.MaterialIndex);
|
|
MaterialProxies.Add(MaterialProxy);
|
|
}
|
|
|
|
// Draw the meshes.
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
|
|
FMeshBatch& Mesh = Collector.AllocateMesh();
|
|
Mesh.bWireframe = bWireframe;
|
|
Mesh.VertexFactory = GetVertexFactory();
|
|
Mesh.MaterialRenderProxy = MaterialProxies[SectionIndex];
|
|
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
|
Mesh.Type = PT_TriangleList;
|
|
Mesh.DepthPriorityGroup = SDPG_World;
|
|
Mesh.bCanApplyViewModeOverrides = true;
|
|
SetDebugMaterial(Mesh);
|
|
|
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
|
BatchElement.IndexBuffer = &MeshResource.IndexBuffer;
|
|
BatchElement.PrimitiveUniformBuffer = GetUniformBuffer();
|
|
BatchElement.FirstIndex = Section.TriangleStart * 3;
|
|
BatchElement.NumPrimitives = Section.TriangleCount;
|
|
BatchElement.MinVertexIndex = Section.VertexStart;
|
|
BatchElement.MaxVertexIndex = Section.VertexEnd;
|
|
|
|
Collector.AddMesh(ViewIndex, Mesh);
|
|
}
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
// Highlight selected bone using specialized material.
|
|
// #note: This renders the geometry again but with the bone selection material. Ideally we'd have one render pass and one material.
|
|
if (bEnableBoneSelection && !bSuppressSelectionMaterial && BoneSelectedMaterial)
|
|
{
|
|
FMaterialRenderProxy* MaterialRenderProxy = BoneSelectedMaterial->GetRenderProxy();
|
|
|
|
FMeshBatch& Mesh = Collector.AllocateMesh();
|
|
Mesh.bWireframe = bWireframe;
|
|
Mesh.VertexFactory = &VertexFactoryDebugColor;
|
|
Mesh.MaterialRenderProxy = MaterialRenderProxy;
|
|
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
|
Mesh.Type = PT_TriangleList;
|
|
Mesh.DepthPriorityGroup = SDPG_World;
|
|
Mesh.bCanApplyViewModeOverrides = false;
|
|
|
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
|
BatchElement.IndexBuffer = &MeshResource.IndexBuffer;
|
|
BatchElement.PrimitiveUniformBuffer = GetUniformBuffer();
|
|
BatchElement.FirstIndex = 0;
|
|
BatchElement.NumPrimitives = MeshDescription.NumTriangles;
|
|
BatchElement.MinVertexIndex = 0;
|
|
BatchElement.MaxVertexIndex = MeshDescription.NumVertices;
|
|
|
|
Collector.AddMesh(ViewIndex, Mesh);
|
|
}
|
|
#endif // GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
}
|
|
}
|
|
|
|
// draw extra stuff ( collision , bounds ... )
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
if (VisibilityMap & (1 << ViewIndex))
|
|
{
|
|
// collision modes
|
|
if (ShowCollisionMeshes(EngineShowFlags) && GeometryCollection && AllowDebugViewmodes())
|
|
{
|
|
FTransform GeomTransform(GetLocalToWorld());
|
|
if (bDrawWireframeCollision)
|
|
{
|
|
GeometryCollectionDebugDraw::DrawWireframe(*GeometryCollection, GeomTransform, Collector, ViewIndex, GetWireframeColor().ToFColor(true));
|
|
}
|
|
else
|
|
{
|
|
FMaterialRenderProxy* CollisionMaterialInstance = new FColoredMaterialRenderProxy(GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(), GetWireframeColor());
|
|
Collector.RegisterOneFrameMaterialProxy(CollisionMaterialInstance);
|
|
GeometryCollectionDebugDraw::DrawSolid(*GeometryCollection, GeomTransform, Collector, ViewIndex, CollisionMaterialInstance);
|
|
}
|
|
}
|
|
|
|
// render bounds
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
FPrimitiveViewRelevance FGeometryCollectionSceneProxy::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 = DrawsVelocity() && Result.bOpaque && Result.bRenderInMainPass;
|
|
|
|
return Result;
|
|
}
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
HHitProxy* FGeometryCollectionSceneProxy::CreateHitProxies(UPrimitiveComponent* Component, TArray<TRefCountPtr<HHitProxy> >& OutHitProxies)
|
|
{
|
|
HHitProxy* DefaultHitProxy = FPrimitiveSceneProxy::CreateHitProxies(Component, OutHitProxies);
|
|
OutHitProxies.Append(HitProxies);
|
|
return DefaultHitProxy;
|
|
}
|
|
#endif
|
|
|
|
void FGeometryCollectionSceneProxy::GetPreSkinnedLocalBounds(FBoxSphereBounds& OutBounds) const
|
|
{
|
|
OutBounds = MeshDescription.PreSkinnedBounds;
|
|
}
|
|
|
|
uint32 FGeometryCollectionSceneProxy::GetAllocatedSize() const
|
|
{
|
|
return FPrimitiveSceneProxy::GetAllocatedSize()
|
|
+ FGeometryCollectionSceneProxyBase::GetAllocatedSize()
|
|
#if WITH_EDITOR
|
|
+ BoneColors.GetAllocatedSize()
|
|
+ (ColorVertexBuffer.GetAllowCPUAccess() ? ColorVertexBuffer.GetStride() * ColorVertexBuffer.GetNumVertices() : 0)
|
|
#endif
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
+ HitProxies.GetAllocatedSize()
|
|
+ (HitProxyIdBuffer.GetAllowCPUAccess() ? HitProxyIdBuffer.GetStride() * HitProxyIdBuffer.GetNumVertices() : 0)
|
|
#endif
|
|
;
|
|
}
|
|
|
|
|
|
FNaniteGeometryCollectionSceneProxy::FNaniteGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component)
|
|
: Nanite::FSceneProxyBase(Component)
|
|
, FGeometryCollectionSceneProxyBase(Component, true)
|
|
, GeometryCollection(Component->GetRestCollection())
|
|
, bRequiresGPUSceneUpdate(false)
|
|
, bEnableBoneSelection(false)
|
|
{
|
|
LLM_SCOPE_BYTAG(Nanite);
|
|
|
|
// Nanite requires GPUScene
|
|
checkSlow(UseGPUScene(GMaxRHIShaderPlatform, GetScene().GetFeatureLevel()));
|
|
checkSlow(DoesPlatformSupportNanite(GMaxRHIShaderPlatform));
|
|
checkSlow(GeometryCollection->HasNaniteData());
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
bEnableBoneSelection = Component->GetEnableBoneSelection();
|
|
#endif
|
|
|
|
FInstanceSceneDataBuffers::FAccessTag AccessTag(PointerHash(this));
|
|
FInstanceSceneDataBuffers::FWriteView ProxyData = InstanceSceneDataBuffersImpl.BeginWriteAccess(AccessTag);
|
|
ProxyData.Flags.bHasPerInstanceHierarchyOffset = true;
|
|
ProxyData.Flags.bHasPerInstanceLocalBounds = true;
|
|
ProxyData.Flags.bHasPerInstanceDynamicData = true;
|
|
ProxyData.Flags.bHasPerInstanceEditorData = bEnableBoneSelection;
|
|
InstanceSceneDataBuffersImpl.EndWriteAccess(AccessTag);
|
|
|
|
// Note: ideally this would be picked up from the Flags.bHasPerInstanceDynamicData above, but that path is not great at the moment.
|
|
bAlwaysHasVelocity = true;
|
|
|
|
// Nanite supports the GPUScene instance data buffer.
|
|
SetupInstanceSceneDataBuffers(&InstanceSceneDataBuffersImpl);
|
|
|
|
bSupportsDistanceFieldRepresentation = false;
|
|
|
|
// Dynamic draw path without Nanite isn't supported by Lumen
|
|
bVisibleInLumenScene = false;
|
|
|
|
// Use fast path that does not update static draw lists.
|
|
bStaticElementsAlwaysUseProxyPrimitiveUniformBuffer = true;
|
|
|
|
// Nanite always uses GPUScene, so we can skip expensive primitive uniform buffer updates.
|
|
bVFRequiresPrimitiveUniformBuffer = false;
|
|
|
|
const TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> Collection = GeometryCollection->GetGeometryCollection();
|
|
const TManagedArray<int32>& TransformToGeometryIndices = Collection->TransformToGeometryIndex;
|
|
const TManagedArray<int32>& SimulationType = Collection->SimulationType;
|
|
const TManagedArray<FGeometryCollectionSection>& SectionsArray = Collection->Sections;
|
|
|
|
MaterialSections.SetNum(SectionsArray.Num());
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionsArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionSection& MeshSection = SectionsArray[SectionIndex];
|
|
const bool bValidMeshSection = MeshSection.MaterialID != INDEX_NONE;
|
|
|
|
// Keep track of highest observed material index.
|
|
MaterialMaxIndex = FMath::Max(MeshSection.MaterialID, MaterialMaxIndex);
|
|
|
|
UMaterialInterface* MaterialInterface = bValidMeshSection ? Component->GetMaterial(MeshSection.MaterialID) : nullptr;
|
|
|
|
// TODO: PROG_RASTER (Implement programmable raster support)
|
|
const bool bInvalidMaterial = !MaterialInterface || !IsOpaqueOrMaskedBlendMode(*MaterialInterface) || MaterialInterface->GetShadingModels().HasShadingModel(MSM_SingleLayerWater);
|
|
if (bInvalidMaterial)
|
|
{
|
|
if (MaterialInterface)
|
|
{
|
|
UE_LOG
|
|
(
|
|
LogStaticMesh, Warning,
|
|
TEXT("Invalid material [%s] used on Nanite geometry collection [%s] - forcing default material instead. Only opaque blend mode and a shading model that is not SingleLayerWater is currently supported, [%s] blend mode and [%s] shading model was specified."),
|
|
*MaterialInterface->GetName(),
|
|
*GeometryCollection->GetName(),
|
|
*GetBlendModeString(MaterialInterface->GetBlendMode()),
|
|
*GetShadingModelFieldString(MaterialInterface->GetShadingModels())
|
|
);
|
|
}
|
|
}
|
|
|
|
if (bInvalidMaterial)
|
|
{
|
|
// force default material
|
|
MaterialInterface = UMaterial::GetDefaultMaterial(MD_Surface);
|
|
}
|
|
|
|
// Should never be null here
|
|
check(MaterialInterface != nullptr);
|
|
|
|
// Should always be opaque blend mode here.
|
|
check(IsOpaqueOrMaskedBlendMode(*MaterialInterface));
|
|
|
|
MaterialSections[SectionIndex].ShadingMaterialProxy = MaterialInterface->GetRenderProxy();
|
|
MaterialSections[SectionIndex].RasterMaterialProxy = MaterialInterface->GetRenderProxy(); // TODO: PROG_RASTER (Implement programmable raster support)
|
|
MaterialSections[SectionIndex].MaterialIndex = MeshSection.MaterialID;
|
|
MaterialSections[SectionIndex].bCastShadow = true;
|
|
}
|
|
|
|
OnMaterialsUpdated();
|
|
|
|
const bool bHasGeometryBoundingBoxes =
|
|
Collection->HasAttribute("BoundingBox", FGeometryCollection::GeometryGroup) &&
|
|
Collection->NumElements(FGeometryCollection::GeometryGroup);
|
|
|
|
const bool bHasTransformBoundingBoxes =
|
|
Collection->NumElements(FGeometryCollection::TransformGroup) &&
|
|
Collection->HasAttribute("BoundingBox", FGeometryCollection::TransformGroup) &&
|
|
Collection->HasAttribute("TransformToGeometryIndex", FGeometryCollection::TransformGroup);
|
|
|
|
int32 NumGeometry = 0;
|
|
if (bHasGeometryBoundingBoxes)
|
|
{
|
|
NumGeometry = Collection->NumElements(FGeometryCollection::GeometryGroup);
|
|
GeometryNaniteData.SetNumUninitialized(NumGeometry);
|
|
|
|
const TManagedArray<FBox>& BoundingBoxes = Collection->GetAttribute<FBox>("BoundingBox", FGeometryCollection::GeometryGroup);
|
|
for (int32 GeometryIndex = 0; GeometryIndex < NumGeometry; ++GeometryIndex)
|
|
{
|
|
FGeometryNaniteData& Instance = GeometryNaniteData[GeometryIndex];
|
|
Instance.HierarchyOffset = GeometryCollection->GetNaniteHierarchyOffset(GeometryIndex);
|
|
Instance.LocalBounds = BoundingBoxes[GeometryIndex];
|
|
}
|
|
}
|
|
else if (bHasTransformBoundingBoxes)
|
|
{
|
|
NumGeometry = GeometryCollection->RenderData->NaniteResourcesPtr->HierarchyRootOffsets.Num();
|
|
GeometryNaniteData.SetNumUninitialized(NumGeometry);
|
|
|
|
const TManagedArray<FBox>& BoundingBoxes = Collection->GetAttribute<FBox>("BoundingBox", FGeometryCollection::TransformGroup);
|
|
const TManagedArray<int32>& TransformToGeometry = Collection->GetAttribute<int32>("TransformToGeometryIndex", FGeometryCollection::TransformGroup);
|
|
const int32 NumTransformToGeometry = TransformToGeometry.Num();
|
|
for (int32 TransformToGeometryIndex = 0; TransformToGeometryIndex < NumTransformToGeometry; ++TransformToGeometryIndex)
|
|
{
|
|
const int32 GeometryIndex = TransformToGeometry[TransformToGeometryIndex];
|
|
if (GeometryIndex > INDEX_NONE)
|
|
{
|
|
FGeometryNaniteData& Instance = GeometryNaniteData[GeometryIndex];
|
|
Instance.HierarchyOffset = GeometryCollection->GetNaniteHierarchyOffset(GeometryIndex);
|
|
Instance.LocalBounds = BoundingBoxes[TransformToGeometryIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
SetWireframeColor(Component->GetWireframeColorForSceneProxy());
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
if (bEnableBoneSelection)
|
|
{
|
|
// Generate a hit proxy per geometry section so that we can perform per bone hit tests.
|
|
HitProxyMode = EHitProxyMode::PerInstance;
|
|
HitProxies.Reserve(NumGeometry);
|
|
for (int32 GeometryIndex = 0; GeometryIndex < NumGeometry; ++GeometryIndex)
|
|
{
|
|
HGeometryCollection* HitProxy = new HGeometryCollection(Component, GeometryIndex);
|
|
HitProxies.Add(HitProxy);
|
|
}
|
|
}
|
|
else if (AActor* Actor = Component->GetOwner())
|
|
{
|
|
// Generate default material hit proxies for simple selection.
|
|
HitProxyMode = Nanite::FSceneProxyBase::EHitProxyMode::MaterialSection;
|
|
for (int32 SectionIndex = 0; SectionIndex < MaterialSections.Num(); ++SectionIndex)
|
|
{
|
|
FMaterialSection& Section = MaterialSections[SectionIndex];
|
|
HHitProxy* HitProxy = new HActor(Actor, Component, SectionIndex, SectionIndex);
|
|
Section.HitProxy = HitProxy;
|
|
HitProxies.Add(HitProxy);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Initialize to rest transforms.
|
|
TArray<FMatrix44f> RestTransforms;
|
|
Component->GetRestTransforms(RestTransforms);
|
|
|
|
CollisionResponse = Component->GetCollisionResponseToChannels();
|
|
|
|
UpdateInstanceSceneDataBuffers(Component->GetRenderMatrix());
|
|
}
|
|
|
|
FNaniteGeometryCollectionSceneProxy::~FNaniteGeometryCollectionSceneProxy()
|
|
{
|
|
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
|
|
{
|
|
FGeometryCollectionSceneProxyBase::CreateRenderThreadResources(RHICmdList);
|
|
|
|
// Should have valid Nanite data at this point.
|
|
NaniteResourceID = GeometryCollection->GetNaniteResourceID();
|
|
NaniteHierarchyOffset = GeometryCollection->GetNaniteHierarchyOffset();
|
|
check(NaniteResourceID != INDEX_NONE && NaniteHierarchyOffset != INDEX_NONE);
|
|
|
|
FGeometryCollectionSceneProxyBase::SetDynamicData_RenderThread(RHICmdList, DynamicData);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::DestroyRenderThreadResources()
|
|
{
|
|
FGeometryCollectionSceneProxyBase::DestroyRenderThreadResources();
|
|
}
|
|
|
|
SIZE_T FNaniteGeometryCollectionSceneProxy::GetTypeHash() const
|
|
{
|
|
static size_t UniquePointer;
|
|
return reinterpret_cast<size_t>(&UniquePointer);
|
|
}
|
|
|
|
FPrimitiveViewRelevance FNaniteGeometryCollectionSceneProxy::GetViewRelevance(const FSceneView* View) const
|
|
{
|
|
LLM_SCOPE_BYTAG(Nanite);
|
|
|
|
FPrimitiveViewRelevance Result;
|
|
Result.bDrawRelevance = IsShown(View) && View->Family->EngineShowFlags.NaniteMeshes;
|
|
Result.bShadowRelevance = IsShadowCast(View);
|
|
Result.bRenderCustomDepth = Nanite::GetSupportsCustomDepthRendering() && ShouldRenderCustomDepth();
|
|
Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
|
|
|
|
// Always render the Nanite mesh data with static relevance.
|
|
Result.bStaticRelevance = true;
|
|
|
|
// dynamic still relevance still must be used when drawing collisions
|
|
Result.bDynamicRelevance = ShowCollisionMeshes(View->Family->EngineShowFlags);
|
|
|
|
// Should always be covered by constructor of Nanite scene proxy.
|
|
Result.bRenderInMainPass = true;
|
|
|
|
#if WITH_EDITOR
|
|
// Only check these in the editor
|
|
Result.bEditorVisualizeLevelInstanceRelevance = IsEditingLevelInstanceChild();
|
|
Result.bEditorStaticSelectionRelevance = (IsSelected() || IsHovered());
|
|
#endif
|
|
|
|
bool bSetDynamicRelevance = false;
|
|
|
|
Result.bOpaque = true;
|
|
|
|
MaterialRelevance.SetPrimitiveViewRelevance(Result);
|
|
Result.bVelocityRelevance = Result.bOpaque && Result.bRenderInMainPass && DrawsVelocity();
|
|
|
|
return Result;
|
|
}
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
HHitProxy* FNaniteGeometryCollectionSceneProxy::CreateHitProxies(UPrimitiveComponent* Component, TArray<TRefCountPtr<HHitProxy>>& OutHitProxies)
|
|
{
|
|
LLM_SCOPE_BYTAG(Nanite);
|
|
OutHitProxies.Append(HitProxies);
|
|
return Super::CreateHitProxies(Component, OutHitProxies);
|
|
}
|
|
#endif
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::DrawStaticElements(FStaticPrimitiveDrawInterface* PDI)
|
|
{
|
|
const FLightCacheInterface* LCI = nullptr;
|
|
DrawStaticElementsInternal(PDI, LCI);
|
|
}
|
|
|
|
uint32 FNaniteGeometryCollectionSceneProxy::GetAllocatedSize() const
|
|
{
|
|
return FPrimitiveSceneProxy::GetAllocatedSize()
|
|
+ FGeometryCollectionSceneProxyBase::GetAllocatedSize();
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::GetNaniteResourceInfo(uint32& ResourceID, uint32& HierarchyOffset, uint32& AssemblyTransformOffset, uint32& ImposterIndex) const
|
|
{
|
|
ResourceID = NaniteResourceID;
|
|
HierarchyOffset = NaniteHierarchyOffset;
|
|
AssemblyTransformOffset = INDEX_NONE; // TODO: Nanite-Assemblies
|
|
ImposterIndex = INDEX_NONE; // Imposters are not supported (yet?)
|
|
}
|
|
|
|
Nanite::FResourceMeshInfo FNaniteGeometryCollectionSceneProxy::GetResourceMeshInfo() const
|
|
{
|
|
Nanite::FResources& NaniteResources = *GeometryCollection->RenderData->NaniteResourcesPtr.Get();
|
|
|
|
Nanite::FResourceMeshInfo OutInfo;
|
|
|
|
OutInfo.NumClusters = NaniteResources.NumClusters;
|
|
OutInfo.NumNodes = NaniteResources.HierarchyNodes.Num();
|
|
OutInfo.NumVertices = NaniteResources.NumInputVertices;
|
|
OutInfo.NumTriangles = NaniteResources.NumInputTriangles;
|
|
OutInfo.NumMaterials = MaterialMaxIndex + 1;
|
|
OutInfo.DebugName = GeometryCollection->GetFName();
|
|
|
|
OutInfo.NumResidentClusters = NaniteResources.NumResidentClusters;
|
|
|
|
// TODO: SegmentMapping
|
|
OutInfo.NumSegments = 0;
|
|
|
|
return MoveTemp(OutInfo);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::UpdateInstanceSceneDataBuffers(const FMatrix& PrimitiveLocalToWorld)
|
|
{
|
|
FInstanceSceneDataBuffers::FAccessTag AccessTag(PointerHash(this));
|
|
FInstanceSceneDataBuffers::FWriteView ProxyData = InstanceSceneDataBuffersImpl.BeginWriteAccess(AccessTag);
|
|
InstanceSceneDataBuffersImpl.SetPrimitiveLocalToWorld(PrimitiveLocalToWorld, AccessTag);
|
|
|
|
const TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> Collection = GeometryCollection->GetGeometryCollection();
|
|
const TManagedArray<int32>& TransformToGeometryIndices = Collection->TransformToGeometryIndex;
|
|
const TManagedArray<TSet<int32>>& TransformChildren = Collection->Children;
|
|
const TManagedArray<int32>& SimulationType = Collection->SimulationType;
|
|
|
|
const int32 TransformCount = DynamicData->Transforms.Num();
|
|
check(TransformCount == TransformToGeometryIndices.Num());
|
|
check(TransformCount == TransformChildren.Num());
|
|
|
|
// set the prev by copying the last current
|
|
ProxyData.PrevInstanceToPrimitiveRelative = ProxyData.InstanceToPrimitiveRelative;
|
|
bCanSkipRedundantTransformUpdates = false; // should we compare the transform to better decide about this ?
|
|
|
|
ProxyData.InstanceToPrimitiveRelative.Reset(TransformCount);
|
|
ProxyData.InstanceLocalBounds.Reset(TransformCount);
|
|
ProxyData.InstanceHierarchyOffset.Reset(TransformCount);
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
ProxyData.InstanceEditorData.Reset(bEnableBoneSelection ? TransformCount : 0);
|
|
#endif
|
|
|
|
ProxyData.Flags.bHasPerInstanceDynamicData = true;
|
|
ProxyData.Flags.bHasPerInstanceLocalBounds = true;
|
|
ProxyData.Flags.bHasPerInstanceHierarchyOffset = true;
|
|
|
|
for (int32 TransformIndex = 0; TransformIndex < TransformCount; ++TransformIndex)
|
|
{
|
|
const int32 TransformToGeometryIndex = TransformToGeometryIndices[TransformIndex];
|
|
if (SimulationType[TransformIndex] != FGeometryCollection::ESimulationTypes::FST_Rigid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FGeometryNaniteData& NaniteData = GeometryNaniteData[TransformToGeometryIndex];
|
|
const FRenderTransform& InstanceToPrimitiveRelative = ProxyData.InstanceToPrimitiveRelative.Emplace_GetRef(InstanceSceneDataBuffersImpl.ComputeInstanceToPrimitiveRelative(DynamicData->Transforms[TransformIndex], AccessTag));
|
|
|
|
ProxyData.InstanceLocalBounds.Emplace(PadInstanceLocalBounds(NaniteData.LocalBounds));
|
|
ProxyData.InstanceHierarchyOffset.Emplace(NaniteData.HierarchyOffset);
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
if (bEnableBoneSelection)
|
|
{
|
|
ProxyData.InstanceEditorData.Emplace(FInstanceEditorData::Pack(HitProxies[TransformToGeometryIndex]->Id.GetColor(), false));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// make sure the previous transform count do match the current one
|
|
// if not simply use the current as previous
|
|
if (ProxyData.PrevInstanceToPrimitiveRelative.Num() != ProxyData.InstanceToPrimitiveRelative.Num())
|
|
{
|
|
ProxyData.PrevInstanceToPrimitiveRelative = ProxyData.InstanceToPrimitiveRelative;
|
|
bCanSkipRedundantTransformUpdates = true;
|
|
}
|
|
|
|
InstanceSceneDataBuffersImpl.EndWriteAccess(AccessTag);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::SetDynamicData_RenderThread(FRHICommandListBase& RHICmdList, FGeometryCollectionDynamicData* NewDynamicData, const FMatrix &PrimitiveLocalToWorld)
|
|
{
|
|
FGeometryCollectionSceneProxyBase::SetDynamicData_RenderThread(RHICmdList, NewDynamicData);
|
|
|
|
UpdateInstanceSceneDataBuffers(PrimitiveLocalToWorld);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::ResetPreviousTransforms_RenderThread()
|
|
{
|
|
FInstanceSceneDataBuffers::FAccessTag AccessTag(PointerHash(this));
|
|
FInstanceSceneDataBuffers::FWriteView ProxyData = InstanceSceneDataBuffersImpl.BeginWriteAccess(AccessTag);
|
|
// Reset previous transforms to avoid locked motion vectors
|
|
// TODO: we should be able to just turn off & delete the prev transforms instead.
|
|
check(ProxyData.InstanceToPrimitiveRelative.Num() == ProxyData.PrevInstanceToPrimitiveRelative.Num()); // Sanity check, should always have matching associated arrays
|
|
for (int32 InstanceIndex = 0; InstanceIndex < ProxyData.InstanceToPrimitiveRelative.Num(); ++InstanceIndex)
|
|
{
|
|
ProxyData.PrevInstanceToPrimitiveRelative[InstanceIndex] = ProxyData.InstanceToPrimitiveRelative[InstanceIndex];
|
|
}
|
|
InstanceSceneDataBuffersImpl.EndWriteAccess(AccessTag);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::FlushGPUSceneUpdate_GameThread()
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(NaniteProxyUpdateGPUScene)(
|
|
[this](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FPrimitiveSceneInfo* NanitePrimitiveInfo = GetPrimitiveSceneInfo();
|
|
if (NanitePrimitiveInfo && GetRequiresGPUSceneUpdate_RenderThread())
|
|
{
|
|
// Attempt to queue up a GPUScene update - maintain dirty flag if the request fails.
|
|
const bool bRequiresUpdate = !NanitePrimitiveInfo->RequestGPUSceneUpdate();
|
|
SetRequiresGPUSceneUpdate_RenderThread(bRequiresUpdate);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
bool FNaniteGeometryCollectionSceneProxy::ShowCollisionMeshes(const FEngineShowFlags& EngineShowFlags) const
|
|
{
|
|
if (IsCollisionEnabled())
|
|
{
|
|
if (EngineShowFlags.CollisionPawn && CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore)
|
|
{
|
|
return true;
|
|
}
|
|
if (EngineShowFlags.CollisionVisibility && CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore)
|
|
{
|
|
return true;
|
|
}
|
|
if (EngineShowFlags.Collision)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_NaniteGeometryCollectionSceneProxy_GetDynamicMeshElements);
|
|
|
|
const FEngineShowFlags& EngineShowFlags = ViewFamily.EngineShowFlags;
|
|
const bool bDrawWireframeCollision = EngineShowFlags.Collision && IsCollisionEnabled();
|
|
|
|
// draw extra stuff ( collision , bounds ... )
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
if (VisibilityMap & (1 << ViewIndex))
|
|
{
|
|
// collision modes
|
|
if (ShowCollisionMeshes(EngineShowFlags) && GeometryCollection && GeometryCollection->GetGeometryCollection() && AllowDebugViewmodes())
|
|
{
|
|
FTransform GeomTransform(GetLocalToWorld());
|
|
if (bDrawWireframeCollision)
|
|
{
|
|
GeometryCollectionDebugDraw::DrawWireframe(*GeometryCollection->GetGeometryCollection(), GeomTransform, Collector, ViewIndex, GetWireframeColor().ToFColor(true));
|
|
}
|
|
else
|
|
{
|
|
FMaterialRenderProxy* CollisionMaterialInstance = new FColoredMaterialRenderProxy(GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(), GetWireframeColor());
|
|
Collector.RegisterOneFrameMaterialProxy(CollisionMaterialInstance);
|
|
GeometryCollectionDebugDraw::DrawSolid(*GeometryCollection->GetGeometryCollection(), GeomTransform, Collector, ViewIndex, CollisionMaterialInstance);
|
|
}
|
|
}
|
|
|
|
// render bounds
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FGeometryCollectionDynamicDataPool::FGeometryCollectionDynamicDataPool()
|
|
{
|
|
FreeList.SetNum(32);
|
|
for (int32 ListIndex = 0; ListIndex < FreeList.Num(); ++ListIndex)
|
|
{
|
|
FreeList[ListIndex] = new FGeometryCollectionDynamicData;
|
|
}
|
|
}
|
|
|
|
FGeometryCollectionDynamicDataPool::~FGeometryCollectionDynamicDataPool()
|
|
{
|
|
FScopeLock ScopeLock(&ListLock);
|
|
|
|
for (FGeometryCollectionDynamicData* Entry : FreeList)
|
|
{
|
|
delete Entry;
|
|
}
|
|
|
|
for (FGeometryCollectionDynamicData* Entry : UsedList)
|
|
{
|
|
delete Entry;
|
|
}
|
|
|
|
FreeList.Empty();
|
|
UsedList.Empty();
|
|
}
|
|
|
|
FGeometryCollectionDynamicData* FGeometryCollectionDynamicDataPool::Allocate()
|
|
{
|
|
FScopeLock ScopeLock(&ListLock);
|
|
|
|
FGeometryCollectionDynamicData* NewEntry = nullptr;
|
|
if (FreeList.Num() > 0)
|
|
{
|
|
NewEntry = FreeList.Pop(EAllowShrinking::No);
|
|
}
|
|
|
|
if (NewEntry == nullptr)
|
|
{
|
|
NewEntry = new FGeometryCollectionDynamicData;
|
|
}
|
|
|
|
NewEntry->Reset();
|
|
UsedList.Push(NewEntry);
|
|
|
|
return NewEntry;
|
|
}
|
|
|
|
void FGeometryCollectionDynamicDataPool::Release(FGeometryCollectionDynamicData* DynamicData)
|
|
{
|
|
FScopeLock ScopeLock(&ListLock);
|
|
|
|
int32 UsedIndex = UsedList.Find(DynamicData);
|
|
if (ensure(UsedIndex != INDEX_NONE))
|
|
{
|
|
UsedList.RemoveAt(UsedIndex, EAllowShrinking::No);
|
|
FreeList.Push(DynamicData);
|
|
}
|
|
}
|
|
|
|
void FGeometryCollectionTransformBuffer::UpdateDynamicData(FRHICommandListBase& RHICmdList, const TArray<FMatrix44f>& Transforms, EResourceLockMode LockMode)
|
|
{
|
|
check(NumTransforms == Transforms.Num());
|
|
|
|
void* VertexBufferData = RHICmdList.LockBuffer(VertexBufferRHI, 0, Transforms.Num() * sizeof(FMatrix44f), LockMode);
|
|
FMemory::Memcpy(VertexBufferData, Transforms.GetData(), Transforms.Num() * sizeof(FMatrix44f));
|
|
RHICmdList.UnlockBuffer(VertexBufferRHI);
|
|
}
|
|
|
|
FNaniteGeometryCollectionSceneProxy::FEmptyLightCacheInfo FNaniteGeometryCollectionSceneProxy::EmptyLightCacheInfo;
|
|
|
|
FLightInteraction FNaniteGeometryCollectionSceneProxy::FEmptyLightCacheInfo::GetInteraction(const FLightSceneProxy* LightSceneProxy) const
|
|
{
|
|
// Ask base class
|
|
TArray<FGuid> Empty_IrrelevantLights;
|
|
ELightInteractionType LightInteraction = GetStaticInteraction(LightSceneProxy, Empty_IrrelevantLights);
|
|
|
|
if (LightInteraction != LIT_MAX)
|
|
{
|
|
return FLightInteraction(LightInteraction);
|
|
}
|
|
|
|
// Use dynamic lighting if the light doesn't have static lighting.
|
|
return FLightInteraction::Dynamic();
|
|
}
|
|
|