You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Fix nanite geometry collection not visually rotating on gizmo driven rotations. Needed to reapply modified instance transforms after primitive transform update. This is because, for nanite, Geom Collection GPU instance transforms are stored relative to the relative (non-rotated) world transform. This bug wasn't visible for non-gizmo use movement cases which go through full scene proxy recreate. This bug wasn't visible for gizmo translation use case because this is in the relative transform. #rb cedric.caillaud [CL 36344776 by jeremy moore in 5.5 branch]
1583 lines
58 KiB
C++
1583 lines
58 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"
|
|
|
|
#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 GRayTracingGeometryCollectionProxyMeshes = 0;
|
|
FAutoConsoleVariableRef CVarRayTracingGeometryCollectionProxyMeshes(
|
|
TEXT("r.RayTracing.Geometry.GeometryCollection"),
|
|
GRayTracingGeometryCollectionProxyMeshes,
|
|
TEXT("Include geometry collection proxy meshes in ray tracing effects (default = 0 (Geometry collection meshes disabled in ray tracing))"),
|
|
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;
|
|
|
|
|
|
class FGeometryCollectionMeshCollectorResources : public FOneFrameResource
|
|
{
|
|
public:
|
|
FGeometryCollectionVertexFactory VertexFactory;
|
|
|
|
FGeometryCollectionMeshCollectorResources(ERHIFeatureLevel::Type InFeatureLevel)
|
|
: VertexFactory(InFeatureLevel,true)
|
|
{
|
|
}
|
|
|
|
virtual ~FGeometryCollectionMeshCollectorResources()
|
|
{
|
|
VertexFactory.ReleaseResource();
|
|
}
|
|
|
|
virtual FGeometryCollectionVertexFactory& GetVertexFactory() { return VertexFactory; }
|
|
};
|
|
|
|
|
|
FGeometryCollectionSceneProxy::FGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component)
|
|
: FPrimitiveSceneProxy(Component)
|
|
, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
|
|
, MeshResource(Component->GetRestCollection()->RenderData->MeshResource)
|
|
, MeshDescription(Component->GetRestCollection()->RenderData->MeshDescription)
|
|
, VertexFactory(GetScene().GetFeatureLevel())
|
|
, bSupportsManualVertexFetch(VertexFactory.SupportsManualVertexFetch(GetScene().GetFeatureLevel()))
|
|
, bSupportsTripleBufferVertexUpload(GRHISupportsMapWriteNoOverwrite)
|
|
#if WITH_EDITOR
|
|
, bShowBoneColors(Component->GetShowBoneColors())
|
|
, bSuppressSelectionMaterial(Component->GetSuppressSelectionMaterial())
|
|
, VertexFactoryDebugColor(GetScene().GetFeatureLevel())
|
|
#endif
|
|
{
|
|
if (Component->GetRestCollection())
|
|
{
|
|
GeometryCollection = Component->GetRestCollection()->GetGeometryCollection();
|
|
}
|
|
|
|
EnableGPUSceneSupportFlags();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
Component->GetRestTransforms(RestTransforms);
|
|
NumTransforms = RestTransforms.Num();
|
|
|
|
#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);
|
|
}
|
|
}
|
|
|
|
// Get hidden geometry and zero the associated transforms.
|
|
Component->GetHiddenTransforms(HiddenTransforms);
|
|
if (HiddenTransforms.Num())
|
|
{
|
|
check(HiddenTransforms.Num() == RestTransforms.Num());
|
|
for (int32 TransformIndex = 0; TransformIndex < RestTransforms.Num(); ++TransformIndex)
|
|
{
|
|
if (HiddenTransforms[TransformIndex])
|
|
{
|
|
RestTransforms[TransformIndex] = FMatrix44f(EForceInit::ForceInitToZero);
|
|
}
|
|
}
|
|
}
|
|
#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;
|
|
|
|
DynamicData = Component->InitDynamicData(true);
|
|
|
|
SetWireframeColor(Component->GetWireframeColorForSceneProxy());
|
|
CollisionResponse = Component->GetCollisionResponseToChannels();
|
|
}
|
|
|
|
FGeometryCollectionSceneProxy::~FGeometryCollectionSceneProxy()
|
|
{
|
|
if (DynamicData != nullptr)
|
|
{
|
|
GDynamicDataPool.Release(DynamicData);
|
|
DynamicData = nullptr;
|
|
}
|
|
}
|
|
|
|
SIZE_T FGeometryCollectionSceneProxy::GetTypeHash() const
|
|
{
|
|
static size_t UniquePointer;
|
|
return reinterpret_cast<size_t>(&UniquePointer);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::SetupVertexFactory(FRHICommandListBase& RHICmdList, FGeometryCollectionVertexFactory& GeometryCollectionVertexFactory, FColorVertexBuffer* ColorOverride) const
|
|
{
|
|
FGeometryCollectionVertexFactory::FDataType Data;
|
|
|
|
FPositionVertexBuffer const& PositionVB = bSupportsManualVertexFetch ? 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 (bSupportsManualVertexFetch)
|
|
{
|
|
Data.BoneMapSRV = MeshResource.BoneMapVertexBuffer.GetSRV();
|
|
Data.BoneTransformSRV = TransformBuffers[CurrentTransformBufferIndex].VertexBufferSRV;
|
|
Data.BonePrevTransformSRV = PrevTransformBuffers[CurrentTransformBufferIndex].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);
|
|
}
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
|
|
{
|
|
if (bSupportsManualVertexFetch)
|
|
{
|
|
// Initialize transform buffers and upload rest transforms.
|
|
TransformBuffers.AddDefaulted(1);
|
|
PrevTransformBuffers.AddDefaulted(1);
|
|
|
|
TransformBuffers[0].NumTransforms = NumTransforms;
|
|
PrevTransformBuffers[0].NumTransforms = NumTransforms;
|
|
TransformBuffers[0].InitResource(RHICmdList);
|
|
PrevTransformBuffers[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);
|
|
FGeometryCollectionTransformBuffer& PrevTransformBuffer = GetCurrentPrevTransformBuffer();
|
|
PrevTransformBuffer.UpdateDynamicData(RHICmdList, DynamicData->PrevTransforms, 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);
|
|
|
|
#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
|
|
|
|
#if RHI_RAYTRACING
|
|
if (IsRayTracingEnabled())
|
|
{
|
|
FRayTracingGeometryInitializer Initializer;
|
|
Initializer.DebugName = FName(TEXT("GeometryCollection"));
|
|
Initializer.GeometryType = RTGT_Triangles;
|
|
Initializer.bFastBuild = true;
|
|
Initializer.bAllowUpdate = false;
|
|
Initializer.TotalPrimitiveCount = 0;
|
|
|
|
RayTracingGeometry.SetInitializer(Initializer);
|
|
RayTracingGeometry.InitResource(RHICmdList);
|
|
|
|
bGeometryResourceUpdated = true;
|
|
}
|
|
#endif
|
|
|
|
bRenderResourcesCreated = true;
|
|
SetDynamicData_RenderThread(RHICmdList, DynamicData);
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::DestroyRenderThreadResources()
|
|
{
|
|
if (bSupportsManualVertexFetch)
|
|
{
|
|
for (int32 i = 0; i < TransformBuffers.Num(); i++)
|
|
{
|
|
TransformBuffers[i].ReleaseResource();
|
|
PrevTransformBuffers[i].ReleaseResource();
|
|
}
|
|
TransformBuffers.Reset();
|
|
}
|
|
else
|
|
{
|
|
SkinnedPositionVertexBuffer.ReleaseResource();
|
|
}
|
|
|
|
VertexFactory.ReleaseResource();
|
|
|
|
#if WITH_EDITOR
|
|
VertexFactoryDebugColor.ReleaseResource();
|
|
ColorVertexBuffer.ReleaseResource();
|
|
#endif
|
|
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
HitProxyIdBuffer.ReleaseResource();
|
|
#endif
|
|
|
|
#if RHI_RAYTRACING
|
|
if (IsRayTracingAllowed())
|
|
{
|
|
RayTracingGeometry.ReleaseResource();
|
|
RayTracingDynamicVertexBuffer.Release();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::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;
|
|
}
|
|
// Early out if if we are applying (non-dynamic) rest transforms over multiple frames.
|
|
if (!DynamicData->IsDynamic && TransformVertexBuffersContainsRestTransforms)
|
|
{
|
|
return;
|
|
}
|
|
TransformVertexBuffersContainsRestTransforms = !DynamicData->IsDynamic;
|
|
|
|
if (bSupportsManualVertexFetch)
|
|
{
|
|
const bool bLocalGeometryCollectionTripleBufferUploads = (GGeometryCollectionTripleBufferUploads != 0) && bSupportsTripleBufferVertexUpload;
|
|
|
|
if (bLocalGeometryCollectionTripleBufferUploads && TransformBuffers.Num() == 1)
|
|
{
|
|
TransformBuffers.AddDefaulted(2);
|
|
PrevTransformBuffers.AddDefaulted(2);
|
|
|
|
for (int32 i = 1; i < 3; i++)
|
|
{
|
|
TransformBuffers[i].NumTransforms = NumTransforms;
|
|
PrevTransformBuffers[i].NumTransforms = NumTransforms;
|
|
TransformBuffers[i].InitResource(RHICmdList);
|
|
PrevTransformBuffers[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();
|
|
FGeometryCollectionTransformBuffer& PrevTransformBuffer = GetCurrentPrevTransformBuffer();
|
|
|
|
VertexFactory.SetBoneTransformSRV(TransformBuffer.VertexBufferSRV);
|
|
VertexFactory.SetBonePrevTransformSRV(PrevTransformBuffer.VertexBufferSRV);
|
|
|
|
#if WITH_EDITOR
|
|
// Implement hiding geometry in editor by zeroing the transform.
|
|
// Could move this to InitDynamicData?
|
|
if (HiddenTransforms.Num())
|
|
{
|
|
for (int32 TransformIndex = 0; TransformIndex < DynamicData->Transforms.Num(); ++TransformIndex)
|
|
{
|
|
if (HiddenTransforms[TransformIndex])
|
|
{
|
|
DynamicData->Transforms[TransformIndex] = FMatrix44f(EForceInit::ForceInitToZero);
|
|
DynamicData->PrevTransforms[TransformIndex] = FMatrix44f(EForceInit::ForceInitToZero);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (DynamicData->IsDynamic)
|
|
{
|
|
TransformBuffer.UpdateDynamicData(RHICmdList, DynamicData->Transforms, LockMode);
|
|
PrevTransformBuffer.UpdateDynamicData(RHICmdList, DynamicData->PrevTransforms, LockMode);
|
|
}
|
|
else
|
|
{
|
|
// If we are rendering the base mesh geometry then use RestTransforms for both current and previous transforms.
|
|
TransformBuffer.UpdateDynamicData(RHICmdList, RestTransforms, LockMode);
|
|
PrevTransformBuffer.UpdateDynamicData(RHICmdList, RestTransforms, LockMode);
|
|
}
|
|
|
|
UpdateLooseParameter(VertexFactory, TransformBuffer.VertexBufferSRV, PrevTransformBuffer.VertexBufferSRV, MeshResource.BoneMapVertexBuffer.GetSRV());
|
|
|
|
#if WITH_EDITOR
|
|
if (bShowBoneColors || bEnableBoneSelection)
|
|
{
|
|
VertexFactoryDebugColor.SetBoneTransformSRV(TransformBuffer.VertexBufferSRV);
|
|
VertexFactoryDebugColor.SetBonePrevTransformSRV(PrevTransformBuffer.VertexBufferSRV);
|
|
UpdateLooseParameter(VertexFactoryDebugColor, TransformBuffer.VertexBufferSRV, PrevTransformBuffer.VertexBufferSRV, MeshResource.BoneMapVertexBuffer.GetSRV());
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateSkinnedPositions(RHICmdList, DynamicData->IsDynamic ? DynamicData->Transforms : RestTransforms);
|
|
}
|
|
|
|
#if RHI_RAYTRACING
|
|
if (IsRayTracingEnabled())
|
|
{
|
|
bGeometryResourceUpdated = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::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);
|
|
}
|
|
|
|
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 StaticMeshRender.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 not dynamic then use the section array with interior fracture surfaces removed.
|
|
bool bRemoveInternalFaces = DynamicData != nullptr && !DynamicData->IsDynamic && MeshDescription.SectionsNoInternal.Num();
|
|
|
|
#if WITH_EDITOR
|
|
// If hiding geometry in editor then we don't remove hidden faces.
|
|
bRemoveInternalFaces &= HiddenTransforms.Num() == 0;
|
|
#endif
|
|
|
|
#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
|
|
}
|
|
}
|
|
}
|
|
|
|
#if RHI_RAYTRACING
|
|
void FGeometryCollectionSceneProxy::GetDynamicRayTracingInstances(FRayTracingInstanceCollector& Collector)
|
|
{
|
|
if (GRayTracingGeometryCollectionProxyMeshes == 0 || MeshDescription.NumVertices == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_GeometryCollectionSceneProxy_GetDynamicRayTracingInstances);
|
|
|
|
const uint32 LODIndex = 0;
|
|
const bool bWireframe = false;
|
|
|
|
//Loose parameter needs to be updated every frame
|
|
FGeometryCollectionMeshCollectorResources* CollectorResources;
|
|
CollectorResources = &Collector.AllocateOneFrameResource<FGeometryCollectionMeshCollectorResources>(GetScene().GetFeatureLevel());
|
|
FGeometryCollectionVertexFactory& GeometryCollectionVertexFactory = CollectorResources->GetVertexFactory();
|
|
|
|
// Render dynamic objects
|
|
if (!GeometryCollectionVertexFactory.GetType()->SupportsRayTracingDynamicGeometry())
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetupVertexFactory(Collector.GetRHICommandList(), GeometryCollectionVertexFactory);
|
|
|
|
// If not dynamic then use the section array with interior fracture surfaces removed.
|
|
const bool bRemoveInternalFaces = DynamicData != nullptr && !DynamicData->IsDynamic && MeshDescription.SectionsNoInternal.Num();
|
|
TArray<FGeometryCollectionMeshElement> const& SectionArray = bRemoveInternalFaces ? MeshDescription.SectionsNoInternal : MeshDescription.Sections;
|
|
|
|
UpdatingRayTracingGeometry_RenderingThread(SectionArray);
|
|
|
|
if (SectionArray.Num() && RayTracingGeometry.IsValid())
|
|
{
|
|
FRayTracingInstance RayTracingInstance;
|
|
RayTracingInstance.Geometry = &RayTracingGeometry;
|
|
RayTracingInstance.InstanceTransforms.Emplace(GetLocalToWorld());
|
|
|
|
// 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);
|
|
}
|
|
|
|
uint32 MaxVertexIndex = 0;
|
|
for (int32 SectionIndex = 0; SectionIndex < SectionArray.Num(); ++SectionIndex)
|
|
{
|
|
const FGeometryCollectionMeshElement& Section = SectionArray[SectionIndex];
|
|
|
|
// Draw the mesh
|
|
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 = GetUniformBuffer();
|
|
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?
|
|
}
|
|
|
|
FRWBuffer* VertexBuffer = RayTracingDynamicVertexBuffer.NumBytes > 0 ? &RayTracingDynamicVertexBuffer : nullptr;
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
void FGeometryCollectionSceneProxy::UpdatingRayTracingGeometry_RenderingThread(TArray<FGeometryCollectionMeshElement> const& InSectionArray)
|
|
{
|
|
FRHICommandList& RHICmdList = FRHICommandListImmediate::Get();
|
|
|
|
if (bGeometryResourceUpdated)
|
|
{
|
|
RayTracingGeometry.Initializer.Segments.Empty();
|
|
RayTracingGeometry.Initializer.TotalPrimitiveCount = 0;
|
|
|
|
for (int 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;
|
|
RayTracingGeometry.Initializer.Segments.Add(Segment);
|
|
RayTracingGeometry.Initializer.TotalPrimitiveCount += Section.TriangleCount;
|
|
}
|
|
|
|
if (RayTracingGeometry.Initializer.TotalPrimitiveCount > 0)
|
|
{
|
|
RayTracingGeometry.Initializer.IndexBuffer = MeshResource.IndexBuffer.IndexBufferRHI;
|
|
// Create the ray tracing geometry but delay the acceleration structure build.
|
|
RayTracingGeometry.CreateRayTracingGeometry(RHICmdList, ERTAccelerationStructureBuildPriority::Skip);
|
|
}
|
|
|
|
bGeometryResourceUpdated = false;
|
|
}
|
|
}
|
|
#endif // RHI_RAYTRACING
|
|
|
|
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()
|
|
+ Materials.GetAllocatedSize()
|
|
+ MeshDescription.Sections.GetAllocatedSize()
|
|
+ MeshDescription.SubSections.GetAllocatedSize()
|
|
+ RestTransforms.GetAllocatedSize()
|
|
+ (SkinnedPositionVertexBuffer.GetAllowCPUAccess() ? SkinnedPositionVertexBuffer.GetStride() * SkinnedPositionVertexBuffer.GetNumVertices() : 0)
|
|
#if WITH_EDITOR
|
|
+ BoneColors.GetAllocatedSize()
|
|
+ (ColorVertexBuffer.GetAllowCPUAccess() ? ColorVertexBuffer.GetStride() * ColorVertexBuffer.GetNumVertices() : 0)
|
|
+ HiddenTransforms.GetAllocatedSize()
|
|
#endif
|
|
#if GEOMETRYCOLLECTION_EDITOR_SELECTION
|
|
+ HitProxies.GetAllocatedSize()
|
|
+ (HitProxyIdBuffer.GetAllowCPUAccess() ? HitProxyIdBuffer.GetStride() * HitProxyIdBuffer.GetNumVertices() : 0)
|
|
#endif
|
|
#if RHI_RAYTRACING
|
|
+ RayTracingGeometry.RawData.GetAllocatedSize()
|
|
#endif
|
|
;
|
|
}
|
|
|
|
|
|
FNaniteGeometryCollectionSceneProxy::FNaniteGeometryCollectionSceneProxy(UGeometryCollectionComponent* Component)
|
|
: Nanite::FSceneProxyBase(Component)
|
|
, GeometryCollection(Component->GetRestCollection())
|
|
, bCurrentlyInMotion(false)
|
|
, bRequiresGPUSceneUpdate(false)
|
|
, bEnableBoneSelection(false)
|
|
{
|
|
LLM_SCOPE_BYTAG(Nanite);
|
|
|
|
// Nanite requires GPUScene
|
|
checkSlow(UseGPUScene(GMaxRHIShaderPlatform, GetScene().GetFeatureLevel()));
|
|
checkSlow(DoesPlatformSupportNanite(GMaxRHIShaderPlatform));
|
|
checkSlow(GeometryCollection->HasNaniteData());
|
|
|
|
MaterialRelevance = Component->GetMaterialRelevance(Component->GetScene()->GetFeatureLevel());
|
|
|
|
#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;
|
|
|
|
// Indicates if 1 or more materials contain settings not supported by Nanite.
|
|
bHasMaterialErrors = false;
|
|
|
|
// Check if the assigned material can be rendered in Nanite. If not, default.
|
|
// TODO: Handle cases like geometry collections adding a "selected geometry" material with translucency.
|
|
const bool IsRenderable = true;// Nanite::FSceneProxy::IsNaniteRenderable(MaterialRelevance);
|
|
|
|
if (!IsRenderable)
|
|
{
|
|
bHasMaterialErrors = true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
bHasMaterialErrors = true;
|
|
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())
|
|
);
|
|
}
|
|
}
|
|
|
|
const bool bForceDefaultMaterial = /*!!FORCE_NANITE_DEFAULT_MATERIAL ||*/ bHasMaterialErrors;
|
|
if (bForceDefaultMaterial)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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 NumTransforms = TransformToGeometry.Num();
|
|
for (int32 TransformIndex = 0; TransformIndex < NumTransforms; ++TransformIndex)
|
|
{
|
|
const int32 GeometryIndex = TransformToGeometry[TransformIndex];
|
|
if (GeometryIndex > INDEX_NONE)
|
|
{
|
|
FGeometryNaniteData& Instance = GeometryNaniteData[GeometryIndex];
|
|
Instance.HierarchyOffset = GeometryCollection->GetNaniteHierarchyOffset(GeometryIndex);
|
|
Instance.LocalBounds = BoundingBoxes[TransformIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
FGeometryCollectionDynamicData* InitDynamicData = Component->InitDynamicData(true);
|
|
SetDynamicData_RenderThread(InitDynamicData, Component->GetRenderMatrix());
|
|
}
|
|
|
|
FNaniteGeometryCollectionSceneProxy::~FNaniteGeometryCollectionSceneProxy()
|
|
{
|
|
if (DynamicData != nullptr)
|
|
{
|
|
GDynamicDataPool.Release(DynamicData);
|
|
DynamicData = nullptr;
|
|
}
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::CreateRenderThreadResources(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// Should have valid Nanite data at this point.
|
|
NaniteResourceID = GeometryCollection->GetNaniteResourceID();
|
|
NaniteHierarchyOffset = GeometryCollection->GetNaniteHierarchyOffset();
|
|
check(NaniteResourceID != INDEX_NONE && NaniteHierarchyOffset != INDEX_NONE);
|
|
}
|
|
|
|
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::GetMemoryFootprint() const
|
|
{
|
|
return sizeof(*this) + GetAllocatedSize();
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::OnTransformChanged(FRHICommandListBase& RHICmdList)
|
|
{
|
|
Super::OnTransformChanged(RHICmdList);
|
|
|
|
SetDynamicData_RenderThread(DynamicData, GetLocalToWorld());
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::GetNaniteResourceInfo(uint32& ResourceID, uint32& HierarchyOffset, uint32& ImposterIndex) const
|
|
{
|
|
ResourceID = NaniteResourceID;
|
|
HierarchyOffset = NaniteHierarchyOffset;
|
|
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.NumHierarchyNodes;
|
|
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::SetDynamicData_RenderThread(FGeometryCollectionDynamicData* NewDynamicData, const FMatrix &PrimitiveLocalToWorld)
|
|
{
|
|
if (NewDynamicData != DynamicData)
|
|
{
|
|
if (DynamicData)
|
|
{
|
|
GDynamicDataPool.Release(DynamicData);
|
|
DynamicData = nullptr;
|
|
}
|
|
DynamicData = NewDynamicData;
|
|
}
|
|
|
|
// Are we currently simulating?
|
|
if (NewDynamicData->IsDynamic)
|
|
{
|
|
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 = NewDynamicData->Transforms.Num();
|
|
check(TransformCount == TransformToGeometryIndices.Num());
|
|
check(TransformCount == TransformChildren.Num());
|
|
check(TransformCount == NewDynamicData->PrevTransforms.Num());
|
|
|
|
|
|
ProxyData.InstanceToPrimitiveRelative.Reset(TransformCount);
|
|
ProxyData.PrevInstanceToPrimitiveRelative.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(NewDynamicData->Transforms[TransformIndex], AccessTag));
|
|
|
|
FRenderTransform& PrevInstanceToPrimitiveRelative = ProxyData.PrevInstanceToPrimitiveRelative.Emplace_GetRef();
|
|
|
|
if (bCurrentlyInMotion)
|
|
{
|
|
PrevInstanceToPrimitiveRelative = InstanceSceneDataBuffersImpl.ComputeInstanceToPrimitiveRelative(NewDynamicData->PrevTransforms[TransformIndex], AccessTag);
|
|
}
|
|
else
|
|
{
|
|
PrevInstanceToPrimitiveRelative = InstanceToPrimitiveRelative;
|
|
}
|
|
|
|
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
|
|
}
|
|
InstanceSceneDataBuffersImpl.EndWriteAccess(AccessTag);
|
|
}
|
|
else
|
|
{
|
|
// Rendering base geometry, use rest transforms rather than simulated transforms.
|
|
// ...
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::OnMotionBegin()
|
|
{
|
|
bCurrentlyInMotion = true;
|
|
bCanSkipRedundantTransformUpdates = false;
|
|
}
|
|
|
|
void FNaniteGeometryCollectionSceneProxy::OnMotionEnd()
|
|
{
|
|
bCurrentlyInMotion = false;
|
|
bCanSkipRedundantTransformUpdates = true;
|
|
ResetPreviousTransforms_RenderThread();
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|