How to create a mesh in code?

by Ray   Last Updated May 22, 2020 19:13 PM

Getting my feet wet with Stride (formerly Xenko), I want to build a level editor where a user can drag the mouse to create walls at runtime.

I found documentation on how to immediate draw vertices per frame, but I want those vertices to be persistently stored for a mesh.

So I tried navigating through the API with IntelliSense, but didn't get further than this:

public class MeshScript : SyncScript
    public override void Start()
        Mesh mesh = new Mesh();
        // The following is what I expected, but the properties do not exist:
        mesh.Vertices = new Vector3[] { ... };
        mesh.Indices = new ushort[] { 0, 1, 2, 0, 2, 3 };

        Model model = new Model();

        Entity.Add(new ModelComponent(model));

    public override void Update() { }

Does anyone have a link or can explain to me which steps are needed to do this?

Tags : mesh stride

Answers 3

The documentation actually has an article for this which I found while writing the question. Basically, vertices and indices are stored in MeshDraw instances, which are then assigned to the Mesh.Draw property.

A short example demonstrates this quickly by using automatically generated mesh data:

MeshDraw meshDraw = GeometricPrimitive.Sphere.New(GraphicsDevice).ToMeshDraw();
Mesh mesh = new Mesh { Draw = meshDraw }; 

The other more elaborate example uses custom vertex data, which is exactly what I wanted:

// Create vertex buffer.
VertexPositionTexture[] vertices = new VertexPositionTexture[3];
vertices[0].Position = new Vector3(0, 0, 1);
vertices[1].Position = new Vector3(0, 1, 0);
vertices[2].Position = new Vector3(0, 1, 1);
Buffer<VertexPositionTexture> vertexBuffer = Stride.Graphics.Buffer.Vertex.New(GraphicsDevice, vertices);

// Create index buffer.
int[] indices = { 0, 2, 1 };
Buffer<int> indexBuffer = Stride.Graphics.Buffer.Index.New(GraphicsDevice, indices);

// Create the mesh with a MeshDraw instance.
Mesh mesh = new Mesh
    Draw = new MeshDraw
        PrimitiveType = PrimitiveType.TriangleList,
        DrawCount = indices.Length,
        IndexBuffer = new IndexBufferBinding(indexBuffer, true, indices.Length),
        VertexBuffers = new[]
            new VertexBufferBinding(vertexBuffer, VertexPositionTexture.Layout, vertexBuffer.ElementCount)

And then you'd just add these Mesh instances to Models which you put into ModelComponents:

// Create the model with the mesh.
Model model = new Model();
Entity.Add(new ModelComponent(model));
May 07, 2020 22:02 PM

I posted example #5 in the mesh-from-code docs that you found.

Since then, my LoadObjTest example has expanded to load WavefrontOBJ files at runtime. It handles the wavefront parsing (with C# code), mesh instantiation, and setting up textures (diffuse, specular, emission). The relevant code is in LoadAsset.cs. If you're looking to get a textured mesh on screen, this is simple runnable reference.

enter image description here

Of particular note to loading WavefrontOBJ and many external formats is the VertexSoup class. Many file formats specify faces with a position and normal per vertex. However, GPU indexed drawing allows only one index per-vertex, so each vertex must be "full configured". Meaning, if edges are sharp, and a vertex position is reused with multiple normal values, in WavefrontOBJ there is only one copy of that vertex position, but in GPU VertexBuffers that position is repeated for every different fully configured instantiation. Instantiating these "unique vertex configurations" is the job of VertexSoup. If you are generating meshes from code algorithms this may not be an issue for you.

@tebjan also suggested using ProceduralModelBase, which can generate tangents, which are important for normal/bump mapping. I have not been able to get the tangent generation working in my example yet, so I may try to switch to that method to see if that's fruitful.

One thing I have not (yet) done in this example is address any way of reusing a generated Model setup for multiple instantiations into the world. And by this i mean, the generated model is not placed into any asset database. The game engine manages this automatically for baked-in assets to avoid wasting CPU and GPU memory. That said, different applications may have different goals here. It might be intended to recreate a unique mesh every time a WavefrontOBJ file its imported (it might have changed externally), or it might be desired to have two different mesh instantiations (if they are somehow editable or modifiable in the game). Regardless, it's an important issue to be aware of.

David Jeske
David Jeske
May 08, 2020 02:13 AM

You can also make your own procedural model by inheriting from PrimitiveProceduralModelBase. This has several advantages:

  • Write less code
  • Available in Game Studio with preview and gizmo in scene editor
  • Properties (including material) are editable in Game Studio
  • Auto-generate tangents and bi-tangents required for advanced lighting
  • Auto-generate bounding box and bounding sphere for correct shadow map bias
  • Auto-generate additional texture coordinate channels
  • Model space position offset
  • Better upload/render performance due to correct data types

enter image description here

The workflow to add the model in Game Studio is then:

  • Add any procedural model (Cube, Plane, etc.)
  • Change the dropdown to your custom procedural model implementation

The workflow in code or script is:

  • Add an instance of the script below to any entity

EDIT: Full sources - including model update per frame - are now available here:

Code of the custom model:

[Display("MyModel")] // This name shows up in the procedural model dropdown list
public class MyProceduralModel : PrimitiveProceduralModelBase
    // A custom property that shows up in Game Studio
    /// <summary>
    /// Gets or sets the size of the model.
    /// </summary>
    public Vector3 Size { get; set; } = Vector3.One;

    protected override GeometricMeshData<VertexPositionNormalTexture> CreatePrimitiveMeshData()
        // First generate the arrays for vertices and indices with the correct size
        var vertexCount = 4;
        var indexCount = 6;
        var vertices = new VertexPositionNormalTexture[vertexCount];
        var indices = new int[indexCount];

        // Create custom vertices, in this case just a quad facing in Y direction
        var normal = Vector3.UnitZ;
        vertices[0] = new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0) * Size, normal, new Vector2(0, 0));
        vertices[1] = new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0) * Size, normal, new Vector2(1, 0));
        vertices[2] = new VertexPositionNormalTexture(new Vector3(-0.5f, -0.5f, 0) * Size, normal, new Vector2(0, 1));
        vertices[3] = new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0) * Size, normal, new Vector2(1, 1));

        // Create custom indices
        indices[0] = 0;
        indices[1] = 1;
        indices[2] = 2;
        indices[3] = 1;
        indices[4] = 3;
        indices[5] = 2;

        // Create the primitive object for further processing by the base class
        return new GeometricMeshData<VertexPositionNormalTexture>(vertices, indices, isLeftHanded: false) { Name = "MyModel" };

Code of the script that adds the model by code:

public class AddMyModelScript : AsyncScript
    // Declared public member fields and properties will show in the game studio
    public float RotationSpeed { get; set; } = 1;

    public override async Task Execute()
        // Setup the custom model
        await CreateMyModel();

        while (Game.IsRunning)
            // Do stuff every new frame
            Entity.Transform.Rotation *= Quaternion.RotationY(MathUtil.DegreesToRadians(RotationSpeed));

            await Script.NextFrame();

    async Task CreateMyModel()
        // The model classes
        var myModel = new MyProceduralModel();
        var model = new Model();
        var modelComponent = new ModelComponent(model);

        // Generate the procedual model
        myModel.Generate(Services, model);

        // Add a meterial
        var material = Content.Load<Material>("MyModel Material");

        // Add everything to the entity
May 12, 2020 14:08 PM

Related Questions

Algorithmically fracture a mesh in Unity3D?

Updated November 27, 2018 11:13 AM

get UV coordinates from texture

Updated July 26, 2015 13:05 PM

Obtaing triangles from a 2D polygon

Updated June 06, 2016 08:05 AM