Skip to content
PRODUCT GITHUB  ↗

Converting Static Meshes

URealtimeMeshStaticMeshConverter (in the RealtimeMeshExt module — see the section overview for the Build.cs dependency) copies geometry between a UStaticMesh asset and RMC. It works in both directions and at two levels of granularity: raw stream sets, or a whole URealtimeMeshSimple with its LODs and materials.

The canonical demonstration is the example actor ARealtimeMeshExample_Simple_StaticMeshConvert (Source/RealtimeMeshExamples/.../Simple/).

Editor vs. runtime. Reading out of a static mesh works at runtime with one caveat (see below). Writing into a static mesh requires the editor — the UE static-mesh build pipeline is editor-only. The converter logs a warning and returns failure if you call the to -static-mesh paths in a cooked/runtime build.


#include "RealtimeMeshStaticMeshConverter.h"
#include "RealtimeMeshSimple.h"
using namespace RealtimeMesh;
FRealtimeMeshStreamSet StreamSet;
FStreamSetStaticMeshConversionOptions Options;
Options.LODIndex = 0;
Options.bWantTangents = true;
Options.bWantUVs = true;
Options.bWantVertexColors = true;
Options.bWantPolyGroups = true; // keeps each static-mesh section as its own poly group
if (URealtimeMeshStaticMeshConverter::CopyStreamSetFromStaticMesh(SourceStaticMesh, StreamSet, Options))
{
URealtimeMeshSimple* Mesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh<URealtimeMeshSimple>();
const FRealtimeMeshBufferSetKey GroupKey =
FRealtimeMeshBufferSetKey::Create(0, FName("FromStaticMesh"));
Mesh->CreateBufferSet(GroupKey, MoveTemp(StreamSet));
}

CopyStreamSetFromStaticMesh has two internal sources and picks the best one available:

  • Source data (the editor MeshDescription) — highest fidelity, but editor-only. Used automatically in-editor when the asset has valid source models.
  • Render data (the cooked LOD render buffers) — this is what’s available at runtime. It has been through build-time vertex splitting and optimization, so it’s slightly lower fidelity, and it only works if the static mesh asset has Allow CPU Access enabled. Without that flag the render buffers aren’t readable on the CPU and the copy fails with a warning.

So: to convert a static mesh at runtime, tick Allow CPU Access on the asset. In-editor it works either way.

CopyRealtimeMeshFromStaticMesh (Blueprint-callable) copies a range of LODs, the materials, and optionally the distance field and Lumen card data straight into a URealtimeMeshSimple:

ERealtimeMeshOutcomePins Outcome;
FRealtimeMeshStaticMeshConversionOptions Options;
Options.MinLODIndex = 0;
Options.MaxLODIndex = REALTIME_MESH_MAX_LOD_INDEX; // all LODs
Options.bWantsMaterials = true;
Options.bWantsDistanceField = true;
Options.bWantsLumenCards = true;
URealtimeMeshSimple* Mesh =
URealtimeMeshStaticMeshConverter::CopyRealtimeMeshFromStaticMesh(
SourceStaticMesh,
GetRealtimeMeshComponent()->InitializeRealtimeMesh<URealtimeMeshSimple>(),
Options,
Outcome);

FRealtimeMeshStaticMeshConversionOptions extends the stream-set options with MinLODIndex / MaxLODIndex (the LOD range to copy), bWantsMaterials, bWantsDistanceField, and bWantsLumenCards. The distance field and card data are copied from the source asset’s render data when present. (A Nanite copy option exists in the header but is commented out — Nanite conversion is an RMC-Pro feature and is not part of this converter.)

Outcome is an ExpandEnumAsExecs pin in Blueprint (Success / Failure branches); in C++ it’s an out-param you check against ERealtimeMeshOutcomePins::Success.


This direction builds a real UStaticMesh asset and is editor-only (it drives UStaticMesh::Build()). Use it to bake authored/generated geometry into an asset — for example, to save a runtime-generated mesh from an editor utility.

#if WITH_EDITOR
FStreamSetStaticMeshConversionOptions Options;
const bool bOk =
URealtimeMeshStaticMeshConverter::CopyStreamSetToStaticMesh(StreamSet, TargetStaticMesh, Options);
#endif

The converter builds a MeshDescription, creates one polygon group per distinct poly-group value (named Material_<n>), lets the build derive the tangent basis, and calls Build(). It keeps your authored normals unless a stream lacked them.

CopyRealtimeMeshToStaticMesh (Blueprint-callable) does the same for an entire URealtimeMeshSimple, spanning all its section groups into one LOD0 mesh description with one polygon group per material slot:

ERealtimeMeshOutcomePins Outcome;
UStaticMesh* Baked =
URealtimeMeshStaticMeshConverter::CopyRealtimeMeshToStaticMesh(
SourceRealtimeMesh,
TargetStaticMeshAsset,
FRealtimeMeshStaticMeshConversionOptions(),
Outcome);

NodeDirectionNotes
Copy Stream Set From Static MeshSM → stream setRuntime needs Allow CPU Access
Copy Stream Set To Static Meshstream set → SMEditor only
Copy Realtime Mesh From Static MeshSM → RealtimeMeshCopies LODs, materials, DF, cards
Copy Realtime Mesh To Static MeshRealtimeMesh → SMEditor only

All four expose a Success/Failure execution pin.