2D meshes: vertices, indices and UVs
Build an ArrayMesh and display it through MeshInstance2D without an external model.
When to use a mesh
MeshInstance2D displays a Mesh resource in the canvas. ArrayMesh gives explicit control over vertices, triangles and UVs. Sprite2D is usually simpler for a rectangular picture; a mesh is useful for custom topology, shapes or coordinated vertex data.
A Mesh does not create collision, a light occluder or navigation. Configure those separately. Consider Polygon2D for an ordinary polygon before implementing your own arrays.
Run a minimal example
Use Reckfell.NET.Sdk 1.0.0 and net10.0. Save the two files below, run dotnet build, attach CanvasAaExample.cs to an Entity2D root in a .rscene level and run it. A white triangle appears at the Viewport center. Keep Antialiasing Disabled for the first run; see antialiasing for renderer differences.
CanvasMeshes.cs
using System;
using Reckfell;
public static class CanvasMeshes
{
public static ArrayMesh Quad(Vector2 size)
{
if (!size.IsFinite() || size.X <= 0 || size.Y <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
Vector2 half = size * 0.5f;
return Build(
new[] { new Vector2(-half.X, -half.Y), new Vector2(half.X, -half.Y), half, new Vector2(-half.X, half.Y) },
new[] { Vector2.Zero, Vector2.Right, Vector2.One, Vector2.Down },
new[] { 0, 1, 2, 0, 2, 3 });
}
public static ArrayMesh Triangle() => Build(
new[] { new Vector2(-45, -40), new Vector2(50, -17), new Vector2(-20, 45) },
new[] { Vector2.Zero, Vector2.Right, Vector2.Down },
new[] { 0, 1, 2 });
private static ArrayMesh Build(Vector2[] vertices, Vector2[] uv, int[] indices)
{
var arrays = new Reckfell.Collections.Array();
arrays.Resize((int)Mesh.ArrayType.Max);
arrays[(int)Mesh.ArrayType.Vertex] = vertices;
arrays[(int)Mesh.ArrayType.TexUV] = uv;
arrays[(int)Mesh.ArrayType.Index] = indices;
var mesh = new ArrayMesh();
mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
return mesh;
}
}CanvasAaExample.cs
using Reckfell;
public partial class CanvasAaExample : Entity2D
{
[Export] public Viewport.Msaa Antialiasing { get; set; } = Viewport.Msaa.Disabled;
public override void _Ready()
{
GetViewport().Msaa2D = Antialiasing;
AddChild(new MeshInstance2D
{
Mesh = CanvasMeshes.Triangle(),
Position = GetViewportRect().Size * 0.5f + new Vector2(0.3f, 0.2f),
Rotation = 0.17f,
Material = new CanvasItemMaterial { LightMode = CanvasItemMaterial.LightModeEnum.Unshaded }
});
}
}Understand the arrays
| Array | Contents |
|---|---|
| Vertex | Local Vector2 values; their origin is separate from the entity position |
| TexUV | One texture-sampling coordinate per vertex |
| Index | Vertex indices grouped in threes to form triangles |
Quad has four vertices and six indices: (0,1,2) and (0,2,3). These two triangles share vertices. Triangle needs three vertices and three indices. Resize the slot array to Mesh.ArrayType.Max first; do not replace it with a compact list of only populated attributes.
Vector2[] in Vertex selects the 2D format. ArrayMesh does not require adding an unnecessary Z component. The example uses the ordinary canvas material for color; a white silhouette needs no Texture. To display an image, set MeshInstance2D.Texture and align UVs with the intended region. These UVs use 0..1 sampling coordinates, not image pixels.
For custom data, the UV count must match the vertex count and each index must be in [0, vertex count). A triangle-list index count must be divisible by three. Correct topology and attributes matter more than assuming any three points will produce the intended picture.
Update and reuse geometry
Quad validates finite positive dimensions before allocating a resource. Validate generator inputs before AddSurfaceFromArrays too. That call adds a surface rather than replacing the previous one. Rebuilding should create a new Mesh or deliberately clear old surfaces; editing a shared resource affects its users.
MeshInstance2D Position, Rotation and Scale transform existing geometry. Do not regenerate vertices every frame for simple movement. Changing a local C# array after uploading a surface does not automatically update GPU data; use the supported update methods or rebuild the resource.
Diagnose invisible geometry
- Check Mesh, a surface, nonempty Vertex and valid Index data.
- Check local coordinates: the geometry may be outside the Viewport.
- Check Visible, Modulate.A, ZIndex and material.
- If an image is missing, check Texture and corresponding UVs; indices are not UVs.
- If collision is expected, create a physics shape separately.