296 lines
No EOL
8.1 KiB
C++
296 lines
No EOL
8.1 KiB
C++
// Copyright 2020 Phyronnaz
|
|
|
|
#include "VoxelDebug/VoxelLineBatchComponent.h"
|
|
#include "VoxelMinimal.h"
|
|
|
|
#include "PrimitiveViewRelevance.h"
|
|
#include "PrimitiveSceneProxy.h"
|
|
#include "Engine/Engine.h"
|
|
#include "MaterialShared.h"
|
|
#include "Materials/Material.h"
|
|
#include "Engine/CollisionProfile.h"
|
|
#include "SceneManagement.h"
|
|
#include "DynamicMeshBuilder.h"
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Num Debug Lines Drawn"), STAT_NumDebugLinesDrawn, STATGROUP_VoxelCounters);
|
|
|
|
UVoxelLineBatchComponent::UVoxelLineBatchComponent()
|
|
{
|
|
bAutoActivate = true;
|
|
bTickInEditor = true;
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
|
|
UPrimitiveComponent::SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
|
|
|
|
bUseEditorCompositing = true;
|
|
SetGenerateOverlapEvents(false);
|
|
|
|
// Ignore streaming updates since GetUsedMaterials() is not implemented.
|
|
bIgnoreStreamingManagerUpdate = true;
|
|
}
|
|
|
|
void UVoxelLineBatchComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
|
{
|
|
VOXEL_FUNCTION_COUNTER();
|
|
|
|
bool bDirty = false;
|
|
// Update the life time of batched lines, removing the lines which have expired.
|
|
for (int32 LineIndex = 0; LineIndex < BatchedLines.Num(); LineIndex++)
|
|
{
|
|
FBatchedLine& Line = BatchedLines[LineIndex];
|
|
if (Line.RemainingLifeTime > 0.0f)
|
|
{
|
|
Line.RemainingLifeTime -= DeltaTime;
|
|
if (Line.RemainingLifeTime <= 0.0f)
|
|
{
|
|
// The line has expired, remove it.
|
|
BatchedLines.RemoveAtSwap(LineIndex--);
|
|
bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the life time of batched points, removing the points which have expired.
|
|
for (int32 PtIndex = 0; PtIndex < BatchedPoints.Num(); PtIndex++)
|
|
{
|
|
FBatchedPoint& Pt = BatchedPoints[PtIndex];
|
|
if (Pt.RemainingLifeTime > 0.0f)
|
|
{
|
|
Pt.RemainingLifeTime -= DeltaTime;
|
|
if (Pt.RemainingLifeTime <= 0.0f)
|
|
{
|
|
// The point has expired, remove it.
|
|
BatchedPoints.RemoveAtSwap(PtIndex--);
|
|
bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the life time of batched meshes, removing the meshes which have expired.
|
|
for (int32 MeshIndex = 0; MeshIndex < BatchedMeshes.Num(); MeshIndex++)
|
|
{
|
|
FBatchedMesh& Mesh = BatchedMeshes[MeshIndex];
|
|
if (Mesh.RemainingLifeTime > 0.0f)
|
|
{
|
|
Mesh.RemainingLifeTime -= DeltaTime;
|
|
if (Mesh.RemainingLifeTime <= 0.0f)
|
|
{
|
|
// The mesh has expired, remove it.
|
|
BatchedMeshes.RemoveAtSwap(MeshIndex--);
|
|
bDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bDirty)
|
|
{
|
|
MarkRenderStateDirty();
|
|
}
|
|
}
|
|
|
|
void UVoxelLineBatchComponent::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift)
|
|
{
|
|
Super::ApplyWorldOffset(InOffset, bWorldShift);
|
|
|
|
VOXEL_FUNCTION_COUNTER();
|
|
|
|
bool bDirty = false;
|
|
for (FBatchedLine& Line : BatchedLines)
|
|
{
|
|
Line.Start += InOffset;
|
|
Line.End += InOffset;
|
|
bDirty = true;
|
|
}
|
|
|
|
for (FBatchedPoint& Point : BatchedPoints)
|
|
{
|
|
Point.Position += InOffset;
|
|
bDirty = true;
|
|
}
|
|
|
|
for (FBatchedMesh& Mesh : BatchedMeshes)
|
|
{
|
|
for (FVector& Vert : Mesh.MeshVerts)
|
|
{
|
|
Vert += InOffset;
|
|
bDirty = true;
|
|
}
|
|
}
|
|
|
|
if (bDirty)
|
|
{
|
|
MarkRenderStateDirty();
|
|
}
|
|
}
|
|
|
|
FPrimitiveSceneProxy* UVoxelLineBatchComponent::CreateSceneProxy()
|
|
{
|
|
if (BatchedLines.Num() == 0 &&
|
|
BatchedPoints.Num() == 0 &&
|
|
BatchedMeshes.Num() == 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return new FVoxelLineBatcherSceneProxy(this);
|
|
}
|
|
|
|
FBoxSphereBounds UVoxelLineBatchComponent::CalcBounds(const FTransform& LocalToWorld) const
|
|
{
|
|
VOXEL_FUNCTION_COUNTER();
|
|
|
|
if (!bCalculateAccurateBounds)
|
|
{
|
|
const FVector BoxExtent(HALF_WORLD_MAX);
|
|
return FBoxSphereBounds(FVector::ZeroVector, BoxExtent, BoxExtent.Size());
|
|
}
|
|
|
|
FBox BBox(ForceInit);
|
|
for (const FBatchedLine& Line : BatchedLines)
|
|
{
|
|
BBox += Line.Start;
|
|
BBox += Line.End;
|
|
}
|
|
|
|
for (const FBatchedPoint& Point : BatchedPoints)
|
|
{
|
|
BBox += Point.Position;
|
|
}
|
|
|
|
for (const FBatchedMesh& Mesh : BatchedMeshes)
|
|
{
|
|
for (const FVector& Vert : Mesh.MeshVerts)
|
|
{
|
|
BBox += Vert;
|
|
}
|
|
}
|
|
|
|
if (BBox.IsValid)
|
|
{
|
|
// Points are in world space, so no need to transform.
|
|
return FBoxSphereBounds(BBox);
|
|
}
|
|
else
|
|
{
|
|
const FVector BoxExtent(1.f);
|
|
return FBoxSphereBounds(LocalToWorld.GetLocation(), BoxExtent, 1.f);
|
|
}
|
|
}
|
|
|
|
void UVoxelLineBatchComponent::Flush()
|
|
{
|
|
VOXEL_FUNCTION_COUNTER();
|
|
|
|
if (BatchedLines.Num() > 0 || BatchedPoints.Num() > 0 || BatchedMeshes.Num() > 0)
|
|
{
|
|
BatchedLines.Empty();
|
|
BatchedPoints.Empty();
|
|
BatchedMeshes.Empty();
|
|
MarkRenderStateDirty();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
FVoxelLineBatcherSceneProxy::FVoxelLineBatcherSceneProxy(const UVoxelLineBatchComponent* InComponent)
|
|
: FPrimitiveSceneProxy(InComponent)
|
|
, Lines(InComponent->BatchedLines)
|
|
, Points(InComponent->BatchedPoints)
|
|
, Meshes(InComponent->BatchedMeshes)
|
|
{
|
|
bWillEverBeLit = false;
|
|
}
|
|
|
|
SIZE_T FVoxelLineBatcherSceneProxy::GetTypeHash() const
|
|
{
|
|
static size_t UniquePointer;
|
|
return reinterpret_cast<size_t>(&UniquePointer);
|
|
}
|
|
|
|
void FVoxelLineBatcherSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
|
{
|
|
VOXEL_RENDER_FUNCTION_COUNTER();
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
if (VisibilityMap & (1 << ViewIndex))
|
|
{
|
|
const FSceneView* View = Views[ViewIndex];
|
|
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
|
|
|
|
INC_DWORD_STAT_BY(STAT_NumDebugLinesDrawn, Lines.Num());
|
|
if (ensure(!PDI->View)) // FSimpleElementCollector does not have a view
|
|
{
|
|
auto* SimpleCollector = static_cast<FSimpleElementCollector*>(PDI);
|
|
auto& BatchedElements = SimpleCollector->BatchedElements; // No support for depth priority; Would need to use TopBatchedElements
|
|
|
|
// Reserve all for thick and not thick - we don't care about losing a bit of memory there
|
|
BatchedElements.AddReserveLines(Lines.Num(), false, false);
|
|
BatchedElements.AddReserveLines(Lines.Num(), false, true);
|
|
for (auto& Line : Lines)
|
|
{
|
|
BatchedElements.AddLine(Line.Start, Line.End, Line.Color, FHitProxyId(), Line.Thickness);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Slow path
|
|
for (auto& Line : Lines)
|
|
{
|
|
PDI->DrawLine(Line.Start, Line.End, Line.Color, Line.DepthPriority, Line.Thickness);
|
|
}
|
|
}
|
|
|
|
for (auto& Point : Points)
|
|
{
|
|
PDI->DrawPoint(Point.Position, Point.Color, Point.PointSize, Point.DepthPriority);
|
|
}
|
|
|
|
for (auto& Mesh : Meshes)
|
|
{
|
|
static FVector3f const PosX(1.f, 0, 0);
|
|
static FVector3f const PosY(0, 1.f, 0);
|
|
static FVector3f const PosZ(0, 0, 1.f);
|
|
|
|
// this seems far from optimal in terms of perf, but it's for debugging
|
|
FDynamicMeshBuilder MeshBuilder(View->GetFeatureLevel());
|
|
|
|
for (int32 VertIdx = 0; VertIdx < Mesh.MeshVerts.Num(); ++VertIdx)
|
|
{
|
|
MeshBuilder.AddVertex(FVector3f(Mesh.MeshVerts[VertIdx]), FVector2f::ZeroVector, PosX, PosY, PosZ, FColor::White);
|
|
}
|
|
for (int32 Idx = 0; Idx < Mesh.MeshIndices.Num(); Idx += 3)
|
|
{
|
|
MeshBuilder.AddTriangle(Mesh.MeshIndices[Idx], Mesh.MeshIndices[Idx + 1], Mesh.MeshIndices[Idx + 2]);
|
|
}
|
|
|
|
FMaterialRenderProxy* const MaterialRenderProxy = new FColoredMaterialRenderProxy(GEngine->DebugMeshMaterial->GetRenderProxy(), Mesh.Color);
|
|
Collector.RegisterOneFrameMaterialProxy(MaterialRenderProxy);
|
|
MeshBuilder.GetMesh(FMatrix::Identity, MaterialRenderProxy, Mesh.DepthPriority, false, false, ViewIndex, Collector);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FPrimitiveViewRelevance FVoxelLineBatcherSceneProxy::GetViewRelevance(const FSceneView* View) const
|
|
{
|
|
FPrimitiveViewRelevance ViewRelevance;
|
|
ViewRelevance.bDrawRelevance = IsShown(View);
|
|
ViewRelevance.bDynamicRelevance = true;
|
|
// ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative
|
|
ViewRelevance.bSeparateTranslucency = true;
|
|
ViewRelevance.bNormalTranslucency = true;
|
|
return ViewRelevance;
|
|
}
|
|
|
|
uint32 FVoxelLineBatcherSceneProxy::GetMemoryFootprint(void) const
|
|
{
|
|
return sizeof(*this) + GetAllocatedSize();
|
|
}
|
|
|
|
uint32 FVoxelLineBatcherSceneProxy::GetAllocatedSize(void) const
|
|
{
|
|
return FPrimitiveSceneProxy::GetAllocatedSize() + Lines.GetAllocatedSize() + Points.GetAllocatedSize() + Meshes.GetAllocatedSize();
|
|
} |