Skip to content
PRODUCT GITHUB  ↗

Structure

Several pieces work together to render and simulate a Realtime Mesh. This page walks the whole object model from the top (the actor in your level) down to the render-thread proxies that actually own the GPU buffers. Understanding this layering makes the rest of this section — sections, materials, LODs, updates, collision — much easier to follow.


ARealtimeMeshActor is the Realtime Mesh analogue of AStaticMeshActor: a convenient base class for an RMC-powered actor. It owns a single URealtimeMeshComponent as its root and adds editor-friendly generation plumbing so you don’t rebuild your mesh on every construction-script tick while dragging the actor around.

It deliberately provides very little on its own. You are free to ignore it and add a URealtimeMeshComponent directly to your own actor class — or place several of them on one actor.

Notable members (see Public/RealtimeMeshActor.h):

  • GetRealtimeMeshComponent() — the owned component.
  • bDeferGeneration — when true, mesh generation moves out of the construction script and into the OnGenerateMesh event, which is far friendlier to editor performance.
  • bFrozen — freezes automatic rebuilding; the mesh can still be changed by explicit function calls.
  • bResetOnRebuild — clears the mesh before the rebuild event runs.
  • Blueprint helpers MakeStream, MakeStreamSet, and MakeMeshBuilder for building geometry from Blueprint.

The example actors in Source/RealtimeMeshExamples/ all derive from ARealtimeMeshExampleActor, which derives from ARealtimeMeshActor. They are the canonical usage reference throughout this section.


URealtimeMeshComponent is the minimum usable piece. It is a UMeshComponent (like UStaticMeshComponent), so it slots into all the normal component machinery — transforms, materials, collision responses, visibility — and you can place one or more on any actor.

The component itself holds almost no geometry. It holds a pointer to a URealtimeMesh and forwards rendering, bounds, and collision to it:

// Create and attach a concrete mesh in one call. The templated overload
// casts the result for you.
URealtimeMeshSimple* RealtimeMesh =
GetRealtimeMeshComponent()->InitializeRealtimeMesh<URealtimeMeshSimple>();

Key members (see Public/RealtimeMeshComponent.h):

  • InitializeRealtimeMesh(TSubclassOf<URealtimeMesh>) / InitializeRealtimeMesh<MeshType>() — creates a new mesh of the given class, owned by the component.
  • SetRealtimeMesh(URealtimeMesh*) — assigns an existing mesh (this is how you share one mesh across components — see below).
  • GetRealtimeMesh() / GetRealtimeMeshAs<T>() — access the mesh.
  • KeepMomentumOnCollisionUpdate and bUpdateNavigationOnCollisionUpdate — collision-update behavior, covered in Collision.

The RealtimeMesh pointer is a replicated property, so the mesh assignment replicates. (Whether the actor replicates at all is opt-in — see the Migration Notes.)


URealtimeMesh is the UObject that holds the mesh. It owns the material slots, manages the collision body, exposes the LOD API, and — crucially — can be shared by multiple URealtimeMeshComponents (and therefore multiple actors), so several instances render the same geometry without duplicating it.

URealtimeMesh itself is Abstract. You always work with a concrete leaf. The hierarchy is:

URealtimeMesh (abstract UObject base)
└── URealtimeMeshManaged (abstract — "we own the data lifecycle for you")
├── URealtimeMeshSimple (core module)
├── URealtimeMeshProcedural (core module)
└── URealtimeMeshDynamic (RealtimeMeshExt module)
URealtimeMesh
└── URealtimeMeshConstructed (RealtimeMeshExt — factory/provider based, NOT Managed)

The Managed tier (URealtimeMeshManaged) is the important middle layer. It lifts everything that Simple, Procedural, and Dynamic share — collision, Nanite/distance-field/Lumen-card storage, section visibility/shadow wrappers, section and group removal — so those leaves only add their own geometry-population style on top. Its concrete leaves are:

  1. URealtimeMeshSimple — the “feed it a StreamSet and forget about it” mesh, similar in feel to ProceduralMeshComponent and older Runtime/Realtime Mesh Components. This is the class used throughout this section’s examples.
  2. URealtimeMeshProcedural — a ProceduralMeshComponent-parity API layered on the same Managed machinery, for code migrating from PMC.
  3. URealtimeMeshDynamic — feeds a UDynamicMesh (GeometryScript) into a Realtime Mesh, so you get GeometryScript authoring in editor and at runtime with RMC’s performance and features.

URealtimeMeshConstructed sits apart: it derives directly from URealtimeMesh (not Managed) and builds its data at runtime through a hierarchical factory/provider structure rather than storing it as asset bytes.

You can subclass URealtimeMesh yourself, or derive from any of the concrete leaves.

For a full treatment of each leaf class and when to reach for it, see Mesh Types.

Note on the old docs: earlier documentation predates the Managed tier and URealtimeMeshProcedural, and it labeled Dynamic and “Composable” as separate “Pro” products. That split no longer exists — Dynamic and Constructed ship in the RealtimeMeshExt module of the same plugin.

Behind every URealtimeMesh UObject is a C++ data object in the RealtimeMesh:: namespace — FRealtimeMesh — that does the real work: it holds the mesh hierarchy, talks to the render thread, and provides rendering and collision data. The UObject is the thin UObject/Blueprint façade; FRealtimeMesh (and its subclasses FRealtimeMeshManaged, FRealtimeMeshSimple, …) is the engine underneath.

You reach it from the UObject with GetMesh() (returns the shared reference) or GetMeshAs<T>():

using namespace RealtimeMesh;
TSharedRef<FRealtimeMeshSimple> Data = RealtimeMesh->GetMeshData(); // Simple's typed accessor

Most everyday work goes through the UObject API, so you rarely touch FRealtimeMesh directly — but it’s what the hierarchy below lives in.

Much like a material slot on a static mesh, FRealtimeMeshMaterialSlot names one of the materials the mesh uses. Each slot has a SlotName and a Material. A renderable section stores the index of the slot it draws with. See Materials.


The mesh hierarchy: LOD → Section Group → Section

Section titled “The mesh hierarchy: LOD → Section Group → Section”

Inside the data layer, geometry is organized as a three-level tree. Each level exists on the game thread (the objects below) and is mirrored on the render thread (the proxies further down).

The top of the geometry hierarchy. A LOD holds an arbitrary number of section groups/sections to render when that level of detail is active.

A LOD’s configuration (FRealtimeMeshLODConfig) carries the ScreenSize at which it activates (when no higher-detail LOD is active) and a bIsVisible flag. LODs are completely independent — each can hold a different number and configuration of section groups and sections. So a high-detail LOD might use many independent sections, while a low-detail LOD collapses down to a single combined section for speed. See Levels of Detail.

The middle of the hierarchy. A section group owns a set of vertex and index buffers (streams) that can be shared by an arbitrary number of sections within the group. Sharing one buffer set across sections both reuses memory and minimizes render-state changes when drawing those sections.

The two names coexist: for the key, the canonical type is FRealtimeMeshBufferSetKey (kept for Blueprint API stability) with FRealtimeMeshBufferSetKey as an alias; for the class, the canonical type is FRealtimeMeshBufferSet with FRealtimeMeshSectionGroup as the back-compat alias. Conceptually the post-flattening role is a buffer set — either spelling refers to the same thing.

The lowest level. A section links a range of the parent group’s vertex/index buffers with a material slot and rendering settings, to draw one portion of the mesh within the active LOD.

Because several sections can draw from the same buffer set, you can render portions of those buffers in different ways — for example one section that draws a subset of the index buffer for shadows, and another that draws the full index buffer for the visible mesh.

  • The section’s stream range (FRealtimeMeshStreamRange) is the start/end element of the vertices and indices it draws from the parent group’s buffers.
  • The section’s config (FRealtimeMeshSectionConfig) sets its material slot, visibility, shadow casting, and a couple of advanced render flags.

Note that the draw type (Static vs Dynamic) is a property of the section group (FRealtimeMeshBufferSetConfig), not the individual section — it governs how the group’s buffers are allocated and updated. Section groups and sections, their keys, and configuration are covered in detail in Section Groups & Sections.


Like other render proxies in the engine, the Realtime Mesh keeps a separate copy of its state on the render thread. This avoids lock contention with the game thread: the game thread mutates its objects and hands the render thread an updated (copy-on-write) mirror rather than sharing live state.

Each game-thread object has a render-thread counterpart:

Game threadRender thread
FRealtimeMeshFRealtimeMeshProxy
FRealtimeMeshLODFRealtimeMeshLODProxy
FRealtimeMeshSectionGroup (buffer set)FRealtimeMeshBufferSetProxy
FRealtimeMeshSectionFRealtimeMeshSectionProxy
URealtimeMeshComponentFRealtimeMeshComponentSceneProxy

FRealtimeMeshProxy owns all the RHI data and works with the renderer to draw the mesh. FRealtimeMeshBufferSetProxy owns the actual RHI vertex/index buffers, vertex factories, and ray-tracing data. FRealtimeMeshSectionProxy clones the section’s configuration so the render thread can set up mesh draws. FRealtimeMeshComponentSceneProxy is the component’s FPrimitiveSceneProxy and links to the FRealtimeMeshProxy to render the mesh.

You almost never touch these directly. They matter because they explain the update model: a Static draw type requires recreating the proxy to change geometry, while Dynamic (and compute-writable) buffers can be updated in place without a full proxy recreate. That distinction drives everything in Updating Mesh Data.