// Copyright Epic Games, Inc. All Rights Reserved. #include "LidarPointCloudRenderBuffers.h" #include "RenderResource.h" #include "VertexFactory.h" #include "MeshBatch.h" #include "RenderCommandFence.h" #include "LidarPointCloudOctree.h" #include "LidarPointCloudSettings.h" #include "MaterialDomain.h" #include "MeshDrawShaderBindings.h" #include "MeshMaterialShader.h" #if WITH_EDITOR #include "Settings/EditorStyleSettings.h" #endif IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FLidarPointCloudVertexFactoryUniformShaderParameters, "LidarVF"); #define BINDPARAM(Name) Name.Bind(ParameterMap, TEXT(#Name)) #define SETPARAM(Name) if (Name.IsBound()) { ShaderBindings.Add(Name, UserData->Name); } #define SETSRVPARAM(Name) if(UserData->Name) { SETPARAM(Name) } //////////////////////////////////////////////////////////// Base Buffer TGlobalResource GLidarPointCloudIndexBuffer; TGlobalResource GLidarPointCloudSharedVertexFactory; TGlobalResource GDummyLidarPointCloudRenderBuffer(4); //////////////////////////////////////////////////////////// Index Buffer void FLidarPointCloudIndexBuffer::Resize(const uint32 & RequestedCapacity) { // This must be called from Rendering thread FRHICommandListImmediate& RHICmdList = FRHICommandListImmediate::Get(); if (Capacity != RequestedCapacity) { ReleaseResource(); Capacity = RequestedCapacity; InitResource(RHICmdList); } } void FLidarPointCloudIndexBuffer::InitRHI(FRHICommandListBase& RHICmdList) { PointOffset = Capacity * 6; const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateIndex(TEXT("FLidarPointCloudIndexBuffer"), Capacity * 7) .AddUsage(EBufferUsageFlags::Dynamic) .SetInitialState(ERHIAccess::VertexOrIndexBuffer) .SetInitActionInitializer(); TRHIBufferInitializer Data = RHICmdList.CreateBufferInitializer(CreateDesc); for (uint32 i = 0, idx = 0; i < Capacity; i++) { const uint32 v = i * 4; // Full quads Data[idx++] = v; Data[idx++] = v + 1; Data[idx++] = v + 2; Data[idx++] = v; Data[idx++] = v + 2; Data[idx++] = v + 3; // Points Data[PointOffset + i] = v; } IndexBufferRHI = Data.Finalize(); } //////////////////////////////////////////////////////////// Structured Buffer void FLidarPointCloudRenderBuffer::Resize(const uint32& RequestedCapacity) { // This must be called from Rendering thread FRHICommandListBase& RHICmdList = FRHICommandListImmediate::Get(); if (Capacity != RequestedCapacity) { ReleaseResource(); Capacity = RequestedCapacity; InitResource(RHICmdList); } else if (!IsInitialized()) { InitResource(RHICmdList); } } void FLidarPointCloudRenderBuffer::InitRHI(FRHICommandListBase& RHICmdList) { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("FLidarPointCloudRenderBuffer"), Capacity) .AddUsage(EBufferUsageFlags::ShaderResource | EBufferUsageFlags::Dynamic) .DetermineInitialState(); Buffer = RHICmdList.CreateBuffer(CreateDesc); SRV = RHICmdList.CreateShaderResourceView( Buffer, FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R32_FLOAT)); FLidarPointCloudVertexFactoryUniformShaderParameters UniformParameters; UniformParameters.VertexFetch_Buffer = SRV; UniformBuffer = TUniformBufferRef::CreateUniformBufferImmediate(UniformParameters, UniformBuffer_MultiFrame); } void FLidarPointCloudRenderBuffer::ReleaseRHI() { // This must be called from Rendering thread check(IsInRenderingThread()); if(UniformBuffer.IsValid()) { UniformBuffer.SafeRelease(); } if (Buffer) { Buffer.SafeRelease(); } SRV.SafeRelease(); } void FLidarPointCloudRenderBuffer::Initialize(FLidarPointCloudPoint* Data, int32 NumPoints) { Resize(NumPoints * 5); FRHICommandListBase& RHICmdList = FRHICommandListImmediate::Get(); uint8* StructuredBuffer = (uint8*)RHICmdList.LockBuffer(Buffer, 0, NumPoints * sizeof(FLidarPointCloudPoint), RLM_WriteOnly); for (FLidarPointCloudPoint* P = Data, *DataEnd = P + NumPoints; P != DataEnd; ++P) { FMemory::Memcpy(StructuredBuffer, P, sizeof(FLidarPointCloudPoint)); StructuredBuffer += sizeof(FLidarPointCloudPoint); } RHICmdList.UnlockBuffer(Buffer); } //////////////////////////////////////////////////////////// Ray Tracing Geometry void FLidarPointCloudRayTracingGeometry::Initialize(int32 NumPoints) { #if RHI_RAYTRACING if (IsInitialized()) { ReleaseResource(); } NumPrimitives = NumPoints * 2; NumVertices = NumPoints * 4; SetInitializer(FRayTracingGeometryInitializer()); Initializer.IndexBuffer = GLidarPointCloudIndexBuffer.IndexBufferRHI; Initializer.TotalPrimitiveCount = NumPrimitives; Initializer.GeometryType = RTGT_Triangles; Initializer.bFastBuild = true; Initializer.bAllowUpdate = true; FRHICommandListBase& RHICmdList = FRHICommandListImmediate::Get(); InitResource(RHICmdList); FRayTracingGeometrySegment Segment; Segment.VertexBuffer = nullptr; Segment.NumPrimitives = NumPrimitives; Segment.MaxVertices = NumVertices; Initializer.Segments.Add(Segment); UpdateRHI(RHICmdList); #endif } //////////////////////////////////////////////////////////// User Data FLidarPointCloudBatchElementUserData::FLidarPointCloudBatchElementUserData() : SelectionColor(FVector::OneVector) , NumClippingVolumes(0) , bStartClipped(false) { for (int32 i = 0; i < 16; ++i) { ClippingVolume[i] = FMatrix44f(FPlane4f(FVector3f::ZeroVector, 0), FPlane4f(FVector3f::ForwardVector, FLT_MAX), FPlane4f(FVector3f::RightVector, FLT_MAX), FPlane4f(FVector3f::UpVector, FLT_MAX)); } #if WITH_EDITOR SelectionColor = FVector3f(GetDefault()->SelectionColor.ToFColor(true)); #endif } void FLidarPointCloudBatchElementUserData::SetClassificationColors(const TMap& InClassificationColors) { for (int32 i = 0; i < 32; ++i) { const FLinearColor* Color = InClassificationColors.Find(i); ClassificationColors[i] = Color ? FVector4f(*Color) : FVector4f(1, 1, 1); } } //////////////////////////////////////////////////////////// Vertex Factory void FLidarPointCloudVertexFactoryShaderParameters::Bind(const FShaderParameterMap& ParameterMap) { BINDPARAM(TreeBuffer); BINDPARAM(bEditorView); BINDPARAM(SelectionColor); BINDPARAM(LocationOffset); BINDPARAM(RootCellSize); BINDPARAM(RootExtent); BINDPARAM(bUsePerPointScaling); BINDPARAM(VirtualDepth); BINDPARAM(SpriteSizeMultiplier); BINDPARAM(ReversedVirtualDepthMultiplier); BINDPARAM(ViewRightVector); BINDPARAM(ViewUpVector); BINDPARAM(bUseCameraFacing); BINDPARAM(bUseScreenSizeScaling); BINDPARAM(bUseStaticBuffers); BINDPARAM(BoundsSize); BINDPARAM(ElevationColorBottom); BINDPARAM(ElevationColorTop); BINDPARAM(bUseCircle); BINDPARAM(bUseColorOverride); BINDPARAM(bUseElevationColor); BINDPARAM(Offset); BINDPARAM(Contrast); BINDPARAM(Saturation); BINDPARAM(Gamma); BINDPARAM(Tint); BINDPARAM(IntensityInfluence); BINDPARAM(bUseClassification); BINDPARAM(bUseClassificationAlpha); BINDPARAM(ClassificationColors); BINDPARAM(ClippingVolume); BINDPARAM(NumClippingVolumes); BINDPARAM(bStartClipped); } void FLidarPointCloudVertexFactoryShaderParameters::GetElementShaderBindings(const class FSceneInterface* Scene, const FSceneView* View, const FMeshMaterialShader* Shader, const EVertexInputStreamType InputStreamType, ERHIFeatureLevel::Type FeatureLevel, const FVertexFactory* VertexFactory, const FMeshBatchElement& BatchElement, class FMeshDrawSingleShaderBindings& ShaderBindings, FVertexInputStreamArray& VertexStreams) const { FRHIUniformBuffer* VertexFactoryUniformBuffer = static_cast(BatchElement.VertexFactoryUserData); FLidarPointCloudBatchElementUserData* UserData = (FLidarPointCloudBatchElementUserData*)BatchElement.UserData; SETSRVPARAM(TreeBuffer); if(VertexFactoryUniformBuffer) { ShaderBindings.Add(Shader->GetUniformBufferParameter(), VertexFactoryUniformBuffer); } SETPARAM(bEditorView); SETPARAM(SelectionColor); SETPARAM(LocationOffset); SETPARAM(RootCellSize); SETPARAM(RootExtent); SETPARAM(bUsePerPointScaling); SETPARAM(VirtualDepth); SETPARAM(SpriteSizeMultiplier); SETPARAM(ReversedVirtualDepthMultiplier); SETPARAM(ViewRightVector); SETPARAM(ViewUpVector); SETPARAM(bUseCameraFacing); SETPARAM(bUseScreenSizeScaling); SETPARAM(bUseStaticBuffers); SETPARAM(BoundsSize); SETPARAM(ElevationColorBottom); SETPARAM(ElevationColorTop); SETPARAM(bUseCircle); SETPARAM(bUseColorOverride); SETPARAM(bUseElevationColor); SETPARAM(Offset); SETPARAM(Contrast); SETPARAM(Saturation); SETPARAM(Gamma); SETPARAM(Tint); SETPARAM(IntensityInfluence); SETPARAM(bUseClassification); SETPARAM(bUseClassificationAlpha); SETPARAM(ClassificationColors); SETPARAM(ClippingVolume); SETPARAM(NumClippingVolumes); SETPARAM(bStartClipped); } bool FLidarPointCloudVertexFactoryBase::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters) { return (IsPCPlatform(Parameters.Platform) && IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && Parameters.MaterialParameters.MaterialDomain == MD_Surface && Parameters.MaterialParameters.bIsUsedWithLidarPointCloud) || Parameters.MaterialParameters.bIsSpecialEngineMaterial; } void FLidarPointCloudVertexFactoryBase::ModifyCompilationEnvironment( const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("RAY_TRACING_DYNAMIC_MESH_IN_LOCAL_SPACE"), TEXT("1")); } FShaderResourceViewRHIRef FLidarPointCloudVertexFactoryBase::GetVertexBufferSRV() { return GDummyLidarPointCloudRenderBuffer.SRV; } void FLidarPointCloudVertexFactoryBase::InitRHI(FRHICommandListBase& RHICmdList) { FLidarPointCloudVertexFactoryUniformShaderParameters UniformParameters; UniformParameters.VertexFetch_Buffer = GetVertexBufferSRV(); if(UniformParameters.VertexFetch_Buffer) { UniformBuffer = TUniformBufferRef::CreateUniformBufferImmediate(UniformParameters, UniformBuffer_MultiFrame); } } void FLidarPointCloudVertexFactoryBase::ReleaseRHI() { UniformBuffer.SafeRelease(); FVertexFactory::ReleaseRHI(); } void FLidarPointCloudVertexFactory::Initialize(FLidarPointCloudPoint* Data, int32 NumPoints) { if (IsInitialized()) { ReleaseResource(); } VertexBuffer.Data = Data; VertexBuffer.NumPoints = NumPoints; InitResource(FRHICommandListImmediate::Get()); } void FLidarPointCloudVertexFactory::FPointCloudVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList) { const uint32 BufferSize = NumPoints * 4 * sizeof(FLidarPointCloudPoint); const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::Create(TEXT("FPointCloudVertexBuffer"), BufferSize, sizeof(FLidarPointCloudPoint), EBufferUsageFlags::VertexBuffer) .AddUsage(EBufferUsageFlags::ShaderResource | EBufferUsageFlags::Static) .SetInitialState(ERHIAccess::VertexOrIndexBuffer) .SetInitActionInitializer(); TRHIBufferInitializer InitialData = RHICmdList.CreateBufferInitializer(CreateDesc); FLidarPointCloudPoint* Dest = InitialData.GetWritableData(); for (int32 i = 0; i < NumPoints; ++i, ++Data) { FMemory::Memcpy(Dest, Data, sizeof(FLidarPointCloudPoint)); Dest++; FMemory::Memcpy(Dest, Data, sizeof(FLidarPointCloudPoint)); Dest++; FMemory::Memcpy(Dest, Data, sizeof(FLidarPointCloudPoint)); Dest++; FMemory::Memcpy(Dest, Data, sizeof(FLidarPointCloudPoint)); Dest++; } VertexBufferRHI = InitialData.Finalize(); VertexBufferSRV = RHICmdList.CreateShaderResourceView( VertexBufferRHI, FRHIViewDesc::CreateBufferSRV() .SetType(FRHIViewDesc::EBufferType::Typed) .SetFormat(PF_R32_FLOAT)); } void FLidarPointCloudVertexFactory::InitRHI(FRHICommandListBase& RHICmdList) { VertexBuffer.InitResource(RHICmdList); FVertexDeclarationElementList Elements; Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 0, sizeof(FLidarPointCloudPoint), VET_Float3), 0)); Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 12, sizeof(FLidarPointCloudPoint), VET_Color), 1)); Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 16, sizeof(FLidarPointCloudPoint), VET_UInt), 2)); InitDeclaration(Elements); FLidarPointCloudVertexFactoryBase::InitRHI(RHICmdList); } void FLidarPointCloudVertexFactory::ReleaseRHI() { FLidarPointCloudVertexFactoryBase::ReleaseRHI(); VertexBuffer.ReleaseResource(); } void FLidarPointCloudSharedVertexFactory::FPointCloudVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList) { const FRHIBufferCreateDesc CreateDesc = FRHIBufferCreateDesc::CreateVertex(TEXT("FPointCloudVertexBuffer"), sizeof(FVector)) .AddUsage(EBufferUsageFlags::Static) .SetInitialState(ERHIAccess::VertexOrIndexBuffer) .SetInitActionZeroData(); VertexBufferRHI = RHICmdList.CreateBuffer(CreateDesc); } void FLidarPointCloudSharedVertexFactory::InitRHI(FRHICommandListBase& RHICmdList) { VertexBuffer.InitResource(RHICmdList); FVertexDeclarationElementList Elements; Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 0, 0, VET_Float3), 0)); Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 0, 0, VET_Color), 1)); Elements.Add(AccessStreamComponent(FVertexStreamComponent(&VertexBuffer, 0, 0, VET_Color), 2)); InitDeclaration(Elements); FLidarPointCloudVertexFactoryBase::InitRHI(RHICmdList); } void FLidarPointCloudSharedVertexFactory::ReleaseRHI() { FLidarPointCloudVertexFactoryBase::ReleaseRHI(); VertexBuffer.ReleaseResource(); } IMPLEMENT_TYPE_LAYOUT(FLidarPointCloudVertexFactoryShaderParameters); IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLidarPointCloudVertexFactoryBase, SF_Vertex, FLidarPointCloudVertexFactoryShaderParameters); #if RHI_RAYTRACING IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLidarPointCloudVertexFactoryBase, SF_Compute, FLidarPointCloudVertexFactoryShaderParameters); IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLidarPointCloudVertexFactoryBase, SF_RayHitGroup, FLidarPointCloudVertexFactoryShaderParameters); #endif IMPLEMENT_VERTEX_FACTORY_TYPE(FLidarPointCloudVertexFactoryBase, "/Plugin/LidarPointCloud/Private/LidarPointCloudVertexFactory.ush", EVertexFactoryFlags::UsedWithMaterials | EVertexFactoryFlags::SupportsDynamicLighting | EVertexFactoryFlags::SupportsPositionOnly | EVertexFactoryFlags::SupportsRayTracingDynamicGeometry ); #undef BINDPARAM #undef SETPARAM #undef SETSRVPARAM