Skip to content
PRODUCT GITHUB  ↗

RealtimeMesh Procedural

URealtimeMeshProcedural mirrors UProceduralMeshComponent’s (PMC’s) Blueprint surface so an existing PMC project can move onto RMC with mostly find-and-replace changes. You supply geometry as parallel TArrays — positions, triangles, normals, UVs, colors, tangents — through CreateMeshSection / UpdateMeshSection, exactly as with PMC. Under the hood each PMC section index maps to its own section group with a single section and independent buffers, matching PMC’s “an update is local to one section” semantics.

Header: Source/RealtimeMeshComponent/Public/RealtimeMeshProcedural.h.

Once you’re running, consider adopting URealtimeMeshSimple for the sections that need polymorphic stream layouts, polygroup auto-sectioning, or minimum-copy per-frame updates — Procedural trades those away for PMC parity.


#include "RealtimeMeshProcedural.h"
URealtimeMeshProcedural* RealtimeMesh = Component->InitializeRealtimeMesh<URealtimeMeshProcedural>();

In Blueprint, use the static InitializeRealtimeMeshProcedural(Owner) node.


CreateMeshSection takes the familiar PMC parameter list. It replaces any existing section at the same index:

TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UV0;
TArray<FColor> VertexColors;
TArray<FRealtimeMeshProceduralTangent> Tangents;
// ... fill the arrays ...
RealtimeMesh->CreateMeshSection(
/*SectionIndex*/ 0,
Vertices, Triangles, Normals, UV0, VertexColors, Tangents,
/*bCreateCollision*/ true);

The ARealtimeMeshExample_Procedural_Basic example (Source/RealtimeMeshExamples/Private/Procedural/RealtimeMeshExample_Procedural_Basic.cpp) builds a box this way (using URealtimeMeshProceduralMeshLibrary::GenerateBoxMesh to fill the arrays, mirroring PMC’s UKismetProceduralMeshLibrary).

CreateMeshSection_LinearColor mirrors PMC’s linear-color overload: it takes FLinearColor vertex colors and up to four UV channels (UV0–UV3):

RealtimeMesh->CreateMeshSection_LinearColor(
0, Vertices, Triangles, Normals,
UV0, UV1, UV2, UV3,
LinearVertexColors, Tangents,
/*bCreateCollision*/ false,
/*bSRGBConversion*/ false);

bSRGBConversion controls how the FLinearColor inputs pack into the FColor vertex stream. It defaults to false (no conversion) so the values round-trip faithfully to the material’s Vertex Color node; pass true if you instead want the stored color to match the swatch as displayed.


UpdateMeshSection updates vertex attributes on an existing section without changing topology — the index count must match the original CreateMeshSection. Any optional array may be left empty to leave that stream unchanged, which is how you cheaply animate just positions:

// Positions only — pass empty arrays for the streams you're not touching.
const TArray<FVector> EmptyNormals;
const TArray<FVector2D> EmptyUVs;
const TArray<FColor> EmptyColors;
const TArray<FRealtimeMeshProceduralTangent> EmptyTangents;
RealtimeMesh->UpdateMeshSection(0, NewVertices, EmptyNormals, EmptyUVs, EmptyColors, EmptyTangents);

There is a matching UpdateMeshSection_LinearColor for the four-UV / linear-color layout. The ARealtimeMeshExample_Procedural_Update example (Source/RealtimeMeshExamples/Private/Procedural/RealtimeMeshExample_Procedural_Update.cpp) creates a grid once, then animates only its vertex positions each tick this way.


  • GetMeshSection(Index, Vertices, Triangles, Normals, UV0..UV3, VertexColors, Tangents) reads a section back out into CPU arrays; returns false if no section exists at that index. (Tangents come back with bFlipTangentY = false — the flip bit is baked into the stored basis at write time and can’t be recovered.)
  • ClearMeshSection(Index) removes one section; ClearAllMeshSections() removes them all. The index space is not compacted — cleared indices become holes.
  • SetMeshSectionVisible(Index, bVisible) / IsMeshSectionVisible(Index) toggle rendering.
  • GetNumSections() returns the highest-ever-allocated index plus one (including holes), matching PMC.

  • CreateMeshSection’s bCreateCollision flag gives that section per-poly (complex) collision, as in PMC.
  • AddCollisionConvexMesh(ConvexVerts) appends a convex hull to the simple-collision shapes; ClearCollisionConvexMeshes() removes them.
  • GetUseComplexAsSimpleCollision() / SetUseComplexAsSimpleCollision(bool) mirror PMC’s bUseComplexAsSimpleCollision. Set it to false if you give the mesh simple shapes and want it simulated.

See Collision for the broader collision model.


A practical checklist:

  1. Swap the component. Replace UProceduralMeshComponent with URealtimeMeshComponent, then create the mesh with InitializeRealtimeMesh<URealtimeMeshProcedural>() (C++) or the InitializeRealtimeMeshProcedural node (Blueprint). Section calls go on the returned URealtimeMeshProcedural, not the component.
  2. Swap the tangent type. Replace FProcMeshTangent with FRealtimeMeshProceduralTangent — it has the same TangentX + bFlipTangentY fields and constructors, so this is a type-name change only. (RMC defines its own so it doesn’t depend on the ProceduralMeshComponent module.)
  3. Keep your CreateMeshSection / UpdateMeshSection calls — the parameter order and the “empty array leaves the stream as-is” update semantics match PMC. The _LinearColor overloads keep the four-UV layout.
  4. Check vertex-color color space. RMC’s bSRGBConversion defaults to false (values round-trip to the material). If your PMC content relied on the engine’s sRGB packing, pass bSRGBConversion = true.
  5. Move collision setup over. bCreateCollision, AddCollisionConvexMesh / ClearCollisionConvexMeshes, and Get/SetUseComplexAsSimpleCollision map one-to-one.
  6. Swap the mesh-generation helper. Use URealtimeMeshProceduralMeshLibrary where you used UKismetProceduralMeshLibrary.

Unsupported PMC extras (secondary buffers, custom render paths, etc.) aren’t exposed here — if you relied on them, that content belongs on URealtimeMeshSimple instead.