I’ve been playing with a procedural mesh based approach. Currently I’m still drawing in world space but on a custom depth pass with the material posted by me above here in this thread.
Performance is pretty good (consider I tested this at home on a MacBook Pro with a 750M), difference between grid turned off and a 50x50 grid is 0.81ms and a 100x100 grid is 1.26ms.
Now this is on world-coords. It would be nice to have the matrix to apply to send it to screen space, I’ll work on this later. Or does any of the Epic devs have a method for that?
GridProceduralMeshComponent.h
#pragma once
#include "ProceduralMeshComponent.h"
#include "GridProceduralMeshComponent.generated.h"
/**
*
*/
UCLASS()
class CTANDROID_API UGridProceduralMeshComponent : public UProceduralMeshComponent
{
GENERATED_BODY()
public:
void UGridProceduralMeshComponent::CreateProcGrid(int numRows, int numColumns, float stepDist, float thickness);
void UpdatePoints(float time);
FVector2D GetNormalOf2DSegment(FVector2D startPoint, FVector2D endPoint);
protected:
TArray<FVector> Vertices;
TArray<int> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
float m_thickness;
int m_numColumns;
int m_numRows;
float m_stepDist;
};
GridProceduralMeshComponent.cpp
#include "GridProceduralMeshComponent.h"
void UGridProceduralMeshComponent::CreateProcGrid(int numRows, int numColumns, float stepDist, float thickness) {
m_thickness = thickness;
m_numColumns = numColumns;
m_numRows = numRows;
m_stepDist = stepDist;
for (size_t c = 1; c < numColumns; c++)
{
for (size_t r = 1; r < numRows; r++)
{
FVector startPoint = FVector(c - 1, (r - 1) * stepDist, 0);
FVector endPoint = FVector(c - 1, r * stepDist, 0);
FVector normal = FVector(GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).X,
GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).Y,
0);
float halfThick = thickness / 2;
int idx = Vertices.Num() - 1;
Vertices.Add(startPoint - (halfThick * normal));
Vertices.Add(endPoint - (halfThick * normal));
Vertices.Add(endPoint + (halfThick * normal));
Vertices.Add(startPoint + (halfThick * normal));
Triangles.Add(1 + idx);
Triangles.Add(2 + idx);
Triangles.Add(4 + idx);
Triangles.Add(2 + idx);
Triangles.Add(3 + idx);
Triangles.Add(4 + idx);
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
UVs.Add(FVector2D(0, 0));
UVs.Add(FVector2D(0, 1));
UVs.Add(FVector2D(1, 1));
UVs.Add(FVector2D(1, 0));
}
}
for (size_t r = 1; r < numRows; r++)
{
for (size_t c = 1; c < numColumns; c++)
{
FVector startPoint = FVector((c - 1) * stepDist, r - 1, 0);
FVector endPoint = FVector(c * stepDist, r - 1 , 0);
FVector normal = FVector(GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).X,
GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).Y,
0);
float halfThick = thickness / 2;
int idx = Vertices.Num();
Vertices.Add(startPoint - (halfThick * normal));
Vertices.Add(endPoint - (halfThick * normal));
Vertices.Add(endPoint + (halfThick * normal));
Vertices.Add(startPoint + (halfThick * normal));
Triangles.Add(0 + idx);
Triangles.Add(1 + idx);
Triangles.Add(3 + idx);
Triangles.Add(1 + idx);
Triangles.Add(2 + idx);
Triangles.Add(3 + idx);
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
Normals.Add(FVector(0, 0, 1));
UVs.Add(FVector2D(0, 0));
UVs.Add(FVector2D(0, 1));
UVs.Add(FVector2D(1, 1));
UVs.Add(FVector2D(1, 0));
}
}
CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray<FColor>(), TArray<FProcMeshTangent>(), false);
}
void UGridProceduralMeshComponent::UpdatePoints(float time) {
Vertices.Reset();
float baseZ = 300;
for (size_t c = 1; c < m_numColumns; c++)
{
for (size_t r = 1; r < m_numRows; r++)
{
FVector startPoint = FVector((c - 1)* m_stepDist, (r - 1) * m_stepDist, 10 * cos(time + r-1) + baseZ);
FVector endPoint = FVector((c - 1)* m_stepDist, r * m_stepDist, 10 * cos(time + r) + baseZ);
FVector normal = FVector(GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).X,
GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).Y,
0);
float halfThick = m_thickness / 2;
int idx = Vertices.Num() - 1;
Vertices.Add(startPoint - (halfThick * normal));
Vertices.Add(endPoint - (halfThick * normal));
Vertices.Add(endPoint + (halfThick * normal));
Vertices.Add(startPoint + (halfThick * normal));
}
}
for (size_t r = 1; r < m_numRows; r++)
{
for (size_t c = 1; c < m_numColumns; c++)
{
FVector startPoint = FVector((c - 1) * m_stepDist, (r - 1)* m_stepDist, 10 * cos(time + r-1) + baseZ);
FVector endPoint = FVector(c * m_stepDist, (r - 1)* m_stepDist, 10 * cos(time + r-1) + baseZ);
FVector normal = FVector(GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).X,
GetNormalOf2DSegment(FVector2D(startPoint.X, startPoint.Y), FVector2D(endPoint.X, endPoint.Y)).Y,
0);
float halfThick = m_thickness / 2;
Vertices.Add(startPoint - (halfThick * normal));
Vertices.Add(endPoint - (halfThick * normal));
Vertices.Add(endPoint + (halfThick * normal));
Vertices.Add(startPoint + (halfThick * normal));
}
}
UpdateMeshSection(0, Vertices, Normals, UVs, TArray<FColor>(), TArray<FProcMeshTangent>());
}
FVector2D UGridProceduralMeshComponent::GetNormalOf2DSegment(FVector2D startPoint, FVector2D endPoint) {
FVector2D norm = FVector2D(endPoint.Y - startPoint.Y, -(endPoint.X - startPoint.X));
return norm.GetSafeNormal();
}
(the video here looks slow, the problem is of the screen capture software I’m using, it’s causing all that spikes on the CPU thread)