MeshBuilder
MeshBuilder builds a MeshDraw at runtime from a vertex layout you
declare and the vertices and indices you add. It allocates and lays out the buffers for you. This
page builds one triangle.
A vertex element is one value per vertex: a Vector3 position, a Color, a Half and so on. Stride
reads it through a VertexElement with a semantic name, a semantic index, an offset and a size. For
a fixed layout, a vertex struct is simpler. Use MeshBuilder when the layout is decided at runtime.
Create a builder
using var meshBuilder = new MeshBuilder();
Note
Dispose the builder. It rents its buffers from ArrayPool<byte>.Shared, and a builder that is not
disposed does not return them.
Layout
The With... methods declare the layout. They must be called before the first vertex is added.
Primitive type
meshBuilder.WithPrimitiveType(PrimitiveType.TriangleList);
Indexing
IndexingType is None, Int16 or Int32. An Int16 index addresses vertices 0 to 32,767.
meshBuilder.WithIndexType(IndexingType.Int16);
Elements
Each With... method returns the element's index, which SetElement takes.
var position = meshBuilder.WithPosition<Vector3>();
var color = meshBuilder.WithColor<Color>();
Vertices
AddVertex() adds a vertex. SetElement sets an element of the last vertex added; the three-argument overload takes a vertex index, so you can add every vertex first and set the values afterwards.
meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(0, 0, 0));
meshBuilder.SetElement(color, Color.Red);
The other two corners follow the same pattern.
meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(1, 0, 0));
meshBuilder.SetElement(color, Color.Green);
meshBuilder.AddVertex();
meshBuilder.SetElement(position, new Vector3(.5f, 1, 0));
meshBuilder.SetElement(color, Color.Blue);
Indices
With indexing configured, AddIndex connects the vertices. With IndexingType.None the vertices are
drawn in the order they were added, three per triangle, and a vertex that two triangles share is
repeated.
Front faces in Stride wind clockwise as seen by the camera - the Direct3D convention
(RasterizerStateDescription.DefaultFrontFaceCounterClockwise is false) - so these indices order
the corners clockwise when viewed from the front. Wind a triangle the other way and it is not drawn
wrong, it is not drawn at all: back-face culling removes it, which shows up as geometry that is
invisible from one side or, on a closed shape, as looking into a hollow shell.
meshBuilder.AddIndex(0);
meshBuilder.AddIndex(2);
meshBuilder.AddIndex(1);
Mesh
ToMeshDraw builds the MeshDraw. It clears the builder afterwards unless clear is false.
var meshDraw = meshBuilder.ToMeshDraw(GraphicsDevice);
Display
Put the MeshDraw in a Model with a material that reads the vertex colour. Without
ComputeVertexStreamColor the triangle is white.
var model = new Model
{
new MaterialInstance {
Material = Material.New(graphicsDevice, new MaterialDescriptor {
Attributes = new MaterialAttributes {
DiffuseModel = new MaterialDiffuseLambertModelFeature(),
Diffuse = new MaterialDiffuseMapFeature {
DiffuseMap = new ComputeVertexStreamColor()
},
}
})
},
new Mesh {
Draw = meshDraw,
MaterialIndex = 0
}
}
Who owns the buffers
ToMeshDraw creates two GPU buffers - vertex and, when indexing is configured, index - and hands
them to you. No content manager tracks them, so if you rebuild a mesh (an animated shape rebuilt
per frame, say) you must dispose the previous draw's buffers yourself, or each rebuild leaks GPU
memory:
foreach (var vertexBuffer in mesh.Draw.VertexBuffers)
vertexBuffer.Buffer.Dispose();
mesh.Draw.IndexBuffer?.Buffer.Dispose();
The Procedural Geometry example shows this pattern on its animated circle.
Extruded shapes and 3D letters
AddExtrudedPolygon turns any simple 2D outline - convex or concave - into a solid: front cap,
back cap and crisp flat-shaded side walls, triangulated internally by ear clipping
(EarClipping.Triangulate, which is public and usable on its own):
var position = meshBuilder.WithPosition<Vector3>();
var normal = meshBuilder.WithNormal<Vector3>();
meshBuilder.AddExtrudedPolygon(outline, depth: 0.25f, position, normal);
Lettering from a real font, holes and all, is 3D Text: TextMesh reads a font's
glyph outlines, triangulates them with EarClipping.Triangulate(outer, holes) and extrudes them the
same way.
Custom pixel formats
Every With* method infers the GPU format from T - Vector3 becomes R32G32B32_Float, Color
becomes R8G8B8A8_UNorm, and so on. For a type the inference does not know, or to store data in a
different format than the CLR type suggests, pass the format explicitly:
var position = meshBuilder.WithPosition<Vector3>(pixelFormat: PixelFormat.R32G32B32A32_Float);
See also
- Procedural Geometry example (
E07_3D_ProceduralGeometry) - 3D Text