Sadly I can’t share a lot of code as it belongs to my employer, but I can point you in the right direction!
If there’s enough interest I could see about releasing it as a C++ on the marketplace as I’d like to see how they work. I’ve also been a bit brief and left out things that you probably already know about, like the Outer being a package, etc.
Start with an FRawMesh:
FRawMesh InRawMesh = FRawMesh();
Then start setting members. This isn’t exactly like a procedural mesh but you can shift the data around from a procedural mesh to make it fit.
The first thing you want to populate is “VertexPositions”. So do that. If you want to be super awesome, during this stage you could de-duplicate your vertices. At the same time build your WedgeIndices by just keeping a TArray of each resulting index that you add to VertexPositions.
Next you populate your wedges. A “wedge” is a corner of a polygon and consists of a vertex position, tangents X-Z (Z = normal, X = tangent, Y = bi-normal), UV coordinates for however many UV channels you have (define at least one!), a vertex colour, etc. Populate all of the wedge members. All of these arrays should be the same length as WedgeIndices (as it is used to reference them) but will exceed the length of VertexPositions.
Finally you have material indices and smoothing groups. You can just fill these with zero if you need to.
You can call “FRawMesh::IsValid()” and “FRawMesh::IsValidOrFixable()” to know in advance if the mesh will work.
If you were making a cube your mesh stats should look a bit like this:
VertexPositions: 8
WedgeIndices: 36
WedgeTangentX, Y and Z: 36
WedgeTexCoords[0]: 36
That’s because each square face has two triangle polys, so six wedges or which two pairs share a vertex.
UE doesn’t really index normals, UVs, etc, only vertex positions.
If all that passes you’re ready to send everything on to your Static Mesh:
auto StaticMesh = NewObject<UStaticMesh>(InOuter, InName, RF_Public | RF_Standalone);
FStaticMeshSourceModel* SrcModel = new(StaticMesh->SourceModels) FStaticMeshSourceModel();
SrcModel->RawMeshBulkData->SaveRawMesh(InRawMesh);
Under SrcModel->BuildSettings you can set up things like lightmap generation, collisions, compute normals, etc. I’d almost always recommend computing normals and just setting the tangents to whatever as a placeholder. UE does a nice job of fixing these up.
Then finish it off:
StaticMesh->Build();
StaticMesh->MarkPackageDirty();
I’ve had to leave out way too much stuff that’s either messy to demonstrate or I’m not able to share but I think most people would find a useful as getting into the guts of UE’s editor isn’t always easy, so I’ll definitely look into that.
I also haven’t looked at skeletal meshes yet either but I assume they’re an entirely new kettle of fish, but it may just be another layer of inheritance on top of a static mesh.