You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Tidy up use of FPrimitiveSceneProxy velocity getters. Now DrawsVelocity() is only for velocity relevance. And HasDynamicTransform() is for determining if we need to store previous transform state. VSM caching was using PRIMITIVE_SCENE_DATA_FLAG_DRAWS_VELOCITY so replaced that with PRIMITIVE_SCENE_DATA_FLAG_SHOULD_CACHE_SHADOW which should use the old behavior and can be tweaked in future using ShouldCacheShadow(). A common pattern for determining whether to output velocity now is to check if a previous transform exists, and to OR in AlwaysHasVelocity(). I found some proxy types that don't check for previous transform. Also I found that the debug physics aggregate types *never* check for previous transform. These are pre-existing potential bugs for fixing in another pass. Also I found some proxies that don't currently fill out velocity relevance. These are pre-existing potential bugs for fixing in another pass. #preflight 62863f789016c6dd897f1cd2 #fyi andrew.lauritzen [CL 20279797 by Jeremy Moore in ue5-main branch]
381 lines
12 KiB
C++
381 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Drawing/PointSetComponent.h"
|
|
#include "RenderingThread.h"
|
|
#include "PrimitiveViewRelevance.h"
|
|
#include "PrimitiveSceneProxy.h"
|
|
#include "VertexFactory.h"
|
|
#include "MaterialShared.h"
|
|
#include "Engine/CollisionProfile.h"
|
|
#include "Materials/Material.h"
|
|
#include "LocalVertexFactory.h"
|
|
#include "SceneManagement.h"
|
|
#include "DynamicMeshBuilder.h"
|
|
#include "StaticMeshResources.h"
|
|
#include "Algo/Accumulate.h"
|
|
#include "Async/ParallelFor.h"
|
|
|
|
|
|
|
|
struct FPointSetMeshBatchData
|
|
{
|
|
FPointSetMeshBatchData()
|
|
: MaterialProxy(nullptr)
|
|
{}
|
|
|
|
FMaterialRenderProxy* MaterialProxy;
|
|
int32 StartIndex;
|
|
int32 NumPrimitives;
|
|
int32 MinVertexIndex;
|
|
int32 MaxVertexIndex;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class FPointSetSceneProxy final : public FPrimitiveSceneProxy
|
|
{
|
|
public:
|
|
|
|
FPointSetSceneProxy(UPointSetComponent* Component)
|
|
: FPrimitiveSceneProxy(Component),
|
|
MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel())),
|
|
VertexFactory(GetScene().GetFeatureLevel(), "FPointSetSceneProxy")
|
|
{
|
|
const int32 NumPointVertices = Component->Points.Num() * 4;
|
|
const int32 NumPointIndices = Component->Points.Num() * 6;
|
|
const int32 TotalNumVertices = NumPointVertices;
|
|
const int32 TotalNumIndices = NumPointIndices;
|
|
const int32 NumTextureCoordinates = 1;
|
|
|
|
VertexBuffers.PositionVertexBuffer.Init(TotalNumVertices);
|
|
VertexBuffers.StaticMeshVertexBuffer.Init(TotalNumVertices, NumTextureCoordinates);
|
|
VertexBuffers.ColorVertexBuffer.Init(TotalNumVertices);
|
|
IndexBuffer.Indices.SetNumUninitialized(TotalNumIndices);
|
|
|
|
int32 VertexBufferIndex = 0;
|
|
int32 IndexBufferIndex = 0;
|
|
|
|
// Initialize points.
|
|
// Points are represented as two tris, all of whose vertices are coincident.
|
|
// The material then offsets them according to the signs of the vertex normals in a camera facing orientation.
|
|
// Size of the point is given by U0.
|
|
if (Component->Points.Num() > 0)
|
|
{
|
|
MeshBatchDatas.Emplace();
|
|
FPointSetMeshBatchData& MeshBatchData = MeshBatchDatas.Last();
|
|
MeshBatchData.MinVertexIndex = VertexBufferIndex;
|
|
MeshBatchData.MaxVertexIndex = VertexBufferIndex + NumPointVertices - 1;
|
|
MeshBatchData.StartIndex = IndexBufferIndex;
|
|
MeshBatchData.NumPrimitives = Component->Points.Num() * 2;
|
|
if (Component->GetMaterial(0) != nullptr)
|
|
{
|
|
MeshBatchData.MaterialProxy = Component->GetMaterial(0)->GetRenderProxy();
|
|
}
|
|
else
|
|
{
|
|
MeshBatchData.MaterialProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
}
|
|
|
|
const FVector TangentVectors[4] = {
|
|
FVector(1.0f, -1.0f, 0.0f),
|
|
FVector(1.0f, 1.0f, 0.0f),
|
|
FVector(-1.0f, 1.0f, 0.0f),
|
|
FVector(-1.0f, -1.0f, 0.0f)
|
|
};
|
|
|
|
// flatten to list of linear points because the set might be very large
|
|
// and we will benefit from parallel processing
|
|
TArray<const FRenderablePoint*> LinearPoints;
|
|
LinearPoints.Reserve(Component->Points.Num());
|
|
for (FRenderablePoint& Point : Component->Points)
|
|
{
|
|
LinearPoints.Add(&Point);
|
|
}
|
|
|
|
// assemble the render buffers
|
|
int NumLinearPoints = LinearPoints.Num();
|
|
ParallelFor(NumLinearPoints, [&](int32 i)
|
|
{
|
|
const FRenderablePoint& Point = *LinearPoints[i];
|
|
int UseVertexBufferIndex = 4 * i;
|
|
int UseIndexBufferIndex = 6 * i;
|
|
|
|
// The color stored in the vertices actually gets interpreted as a linear color by the material,
|
|
// whereas it is more convenient for the user of the LineSet to specify colors as sRGB. So we actually
|
|
// have to convert it back to linear. The ToFColor(false) call just scales back into 0-255 space.
|
|
FColor color = FLinearColor::FromSRGBColor(Point.Color).ToFColor(false);
|
|
|
|
const FVector2f UV(Point.Size, Point.DepthBias);
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
VertexBuffers.PositionVertexBuffer.VertexPosition(UseVertexBufferIndex + j) = (FVector3f)Point.Position;
|
|
VertexBuffers.StaticMeshVertexBuffer.SetVertexUV(UseVertexBufferIndex + j, 0, UV);
|
|
VertexBuffers.ColorVertexBuffer.VertexColor(UseVertexBufferIndex + j) = color;
|
|
VertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(UseVertexBufferIndex + j, FVector3f::ZeroVector, FVector3f::ZeroVector, (FVector3f)TangentVectors[j]);
|
|
}
|
|
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 0] = UseVertexBufferIndex + 0;
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 1] = UseVertexBufferIndex + 1;
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 2] = UseVertexBufferIndex + 2;
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 3] = UseVertexBufferIndex + 2;
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 4] = UseVertexBufferIndex + 3;
|
|
IndexBuffer.Indices[UseIndexBufferIndex + 5] = UseVertexBufferIndex + 0;
|
|
});
|
|
}
|
|
|
|
ENQUEUE_RENDER_COMMAND(OverlayVertexBuffersInit)(
|
|
[this](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
VertexBuffers.PositionVertexBuffer.InitResource();
|
|
VertexBuffers.StaticMeshVertexBuffer.InitResource();
|
|
VertexBuffers.ColorVertexBuffer.InitResource();
|
|
|
|
FLocalVertexFactory::FDataType Data;
|
|
VertexBuffers.PositionVertexBuffer.BindPositionVertexBuffer(&VertexFactory, Data);
|
|
VertexBuffers.StaticMeshVertexBuffer.BindTangentVertexBuffer(&VertexFactory, Data);
|
|
VertexBuffers.StaticMeshVertexBuffer.BindTexCoordVertexBuffer(&VertexFactory, Data);
|
|
VertexBuffers.ColorVertexBuffer.BindColorVertexBuffer(&VertexFactory, Data);
|
|
VertexFactory.SetData(Data);
|
|
|
|
VertexFactory.InitResource();
|
|
IndexBuffer.InitResource();
|
|
});
|
|
}
|
|
|
|
virtual ~FPointSetSceneProxy()
|
|
{
|
|
VertexBuffers.PositionVertexBuffer.ReleaseResource();
|
|
VertexBuffers.StaticMeshVertexBuffer.ReleaseResource();
|
|
VertexBuffers.ColorVertexBuffer.ReleaseResource();
|
|
IndexBuffer.ReleaseResource();
|
|
VertexFactory.ReleaseResource();
|
|
}
|
|
|
|
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_OverlaySceneProxy_GetDynamicMeshElements);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
if (VisibilityMap & (1 << ViewIndex))
|
|
{
|
|
for (const FPointSetMeshBatchData& MeshBatchData : MeshBatchDatas)
|
|
{
|
|
FMeshBatch& Mesh = Collector.AllocateMesh();
|
|
FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
|
BatchElement.IndexBuffer = &IndexBuffer;
|
|
Mesh.bWireframe = false;
|
|
Mesh.VertexFactory = &VertexFactory;
|
|
Mesh.MaterialRenderProxy = MeshBatchData.MaterialProxy;
|
|
|
|
FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer = Collector.AllocateOneFrameResource<FDynamicPrimitiveUniformBuffer>();
|
|
DynamicPrimitiveUniformBuffer.Set(GetLocalToWorld(), GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, false, AlwaysHasVelocity());
|
|
BatchElement.PrimitiveUniformBufferResource = &DynamicPrimitiveUniformBuffer.UniformBuffer;
|
|
|
|
BatchElement.FirstIndex = MeshBatchData.StartIndex;
|
|
BatchElement.NumPrimitives = MeshBatchData.NumPrimitives;
|
|
BatchElement.MinVertexIndex = MeshBatchData.MinVertexIndex;
|
|
BatchElement.MaxVertexIndex = MeshBatchData.MaxVertexIndex;
|
|
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
|
Mesh.Type = PT_TriangleList;
|
|
Mesh.DepthPriorityGroup = SDPG_World;
|
|
Mesh.bCanApplyViewModeOverrides = false;
|
|
Collector.AddMesh(ViewIndex, Mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
|
{
|
|
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;
|
|
}
|
|
|
|
virtual bool CanBeOccluded() const override
|
|
{
|
|
return !MaterialRelevance.bDisableDepthTest;
|
|
}
|
|
|
|
virtual uint32 GetMemoryFootprint() const override { return sizeof(*this) + GetAllocatedSize(); }
|
|
|
|
uint32 GetAllocatedSize() const { return FPrimitiveSceneProxy::GetAllocatedSize(); }
|
|
|
|
virtual SIZE_T GetTypeHash() const override
|
|
{
|
|
static SIZE_T UniquePointer;
|
|
return reinterpret_cast<SIZE_T>(&UniquePointer);
|
|
}
|
|
|
|
private:
|
|
TArray<FPointSetMeshBatchData> MeshBatchDatas;
|
|
FMaterialRelevance MaterialRelevance;
|
|
FLocalVertexFactory VertexFactory;
|
|
FStaticMeshVertexBuffers VertexBuffers;
|
|
FDynamicMeshIndexBuffer32 IndexBuffer;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
UPointSetComponent::UPointSetComponent()
|
|
{
|
|
CastShadow = false;
|
|
bSelectable = false;
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
bBoundsDirty = true;
|
|
|
|
UPrimitiveComponent::SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
|
|
}
|
|
|
|
void UPointSetComponent::SetPointMaterial(UMaterialInterface* InPointMaterial)
|
|
{
|
|
PointMaterial = InPointMaterial;
|
|
UMeshComponent::SetMaterial(0, InPointMaterial);
|
|
}
|
|
|
|
|
|
void UPointSetComponent::Clear()
|
|
{
|
|
Points.Reset();
|
|
MarkRenderStateDirty();
|
|
bBoundsDirty = true;
|
|
}
|
|
|
|
|
|
void UPointSetComponent::ReservePoints(const int32 MaxID)
|
|
{
|
|
Points.Reserve(MaxID);
|
|
}
|
|
|
|
|
|
|
|
int32 UPointSetComponent::AddPoint(const FRenderablePoint& OverlayPoint)
|
|
{
|
|
const int32 ID(Points.Add(OverlayPoint));
|
|
MarkRenderStateDirty();
|
|
bBoundsDirty = true;
|
|
return ID;
|
|
}
|
|
|
|
|
|
void UPointSetComponent::InsertPoint(const int32 ID, const FRenderablePoint& OverlayPoint)
|
|
{
|
|
Points.Insert(ID, OverlayPoint);
|
|
MarkRenderStateDirty();
|
|
bBoundsDirty = true;
|
|
}
|
|
|
|
|
|
const FRenderablePoint& UPointSetComponent::GetPoint(const int32 ID)
|
|
{
|
|
return Points[ID];
|
|
}
|
|
|
|
|
|
void UPointSetComponent::SetPointColor(const int32 ID, const FColor& NewColor)
|
|
{
|
|
FRenderablePoint& OverlayPoint = Points[ID];
|
|
OverlayPoint.Color = NewColor;
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
|
|
void UPointSetComponent::SetPointSize(const int32 ID, const float NewSize)
|
|
{
|
|
FRenderablePoint& OverlayPoint = Points[ID];
|
|
OverlayPoint.Size = NewSize;
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
|
|
void UPointSetComponent::SetPointPosition(const int32 ID, const FVector& NewPosition)
|
|
{
|
|
FRenderablePoint& OverlayPoint = Points[ID];
|
|
OverlayPoint.Position = NewPosition;
|
|
MarkRenderStateDirty();
|
|
bBoundsDirty = true;
|
|
}
|
|
|
|
void UPointSetComponent::SetAllPointsColor(const FColor& NewColor)
|
|
{
|
|
for (FRenderablePoint& Point : Points)
|
|
{
|
|
Point.Color = NewColor;
|
|
}
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
|
|
void UPointSetComponent::RemovePoint(const int32 ID)
|
|
{
|
|
Points.RemoveAt(ID);
|
|
MarkRenderStateDirty();
|
|
bBoundsDirty = true;
|
|
}
|
|
|
|
|
|
bool UPointSetComponent::IsPointValid(const int32 ID) const
|
|
{
|
|
return Points.IsValidIndex(ID);
|
|
}
|
|
|
|
|
|
FPrimitiveSceneProxy* UPointSetComponent::CreateSceneProxy()
|
|
{
|
|
if (Points.Num() > 0)
|
|
{
|
|
return new FPointSetSceneProxy(this);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
int32 UPointSetComponent::GetNumMaterials() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
FBoxSphereBounds UPointSetComponent::CalcBounds(const FTransform& LocalToWorld) const
|
|
{
|
|
if (bBoundsDirty)
|
|
{
|
|
FBox Box(ForceInit);
|
|
for (const FRenderablePoint& Point : Points)
|
|
{
|
|
Box += Point.Position;
|
|
}
|
|
Bounds = FBoxSphereBounds(Box);
|
|
bBoundsDirty = false;
|
|
|
|
// TODO: This next bit is not ideal because the point size is specified in onscreen pixels,
|
|
// so the true amount by which we would need to expand bounds depends on camera location, FOV, etc.
|
|
// We mainly do this as a hack against a problem in ortho viewports, which cull small items
|
|
// based on their bounds, and a set consisting of a single point will always be culled due
|
|
// to having 0-sized bounds. It's worth noting that when zooming out sufficiently far, the
|
|
// point will still be culled even with this hack, however.
|
|
// The proper solution is to be able to opt out of the ortho culling behavior, which is something
|
|
// we need to add.
|
|
if (Points.Num() > 0)
|
|
{
|
|
Bounds = Bounds.ExpandBy(Points[0].Size);
|
|
}
|
|
}
|
|
return Bounds.TransformBy(LocalToWorld);
|
|
}
|
|
|