reckfellENGINE

ArrayMesh: create a surface from arrays

A complete quad with normals, UVs and indices; array layout, winding and resource saving.

On this page

Run the quad

Add GeometryStage3D.cs and ArrayQuadLab3D.cs to a 1.0.0 C# project. Attach ArrayQuadLab3D to a separate Entity3D-root level, set a 640×420 viewport, build and press F6. A blue quad appears above the gray floor. It has one surface, four vertices and six indices.

An ArrayMesh quad with four vertices and two triangles

GeometryStage3D.cs
C#
using Reckfell;

public static class GeometryStage3D
{
    public static StandardMaterial3D Material(bool vertexColors = false, bool unshaded = false)
    {
        return new StandardMaterial3D
        {
            AlbedoColor = vertexColors ? Colors.White : new Color(0.24f, 0.44f, 1),
            VertexColorUseAsAlbedo = vertexColors,
            ShadingMode = unshaded ? BaseMaterial3D.ShadingModeEnum.Unshaded : BaseMaterial3D.ShadingModeEnum.PerPixel,
            Roughness = 0.8f
        };
    }

    public static void Setup(Entity3D root, string ru, string en)
    {
        root.AddChild(new WorldEnvironment { Environment = new Reckfell.Environment
        {
            BackgroundMode = Reckfell.Environment.BGMode.Color,
            BackgroundColor = new Color(0.035f, 0.04f, 0.05f),
            AmbientLightSource = Reckfell.Environment.AmbientSource.Color,
            AmbientLightColor = Colors.White, AmbientLightEnergy = 0.5f
        }});
        root.AddChild(new DirectionalLight3D { RotationDegrees = new Vector3(-55, -25, 0), LightEnergy = 1.2f });
        root.AddChild(new MeshInstance3D
        {
            Mesh = new BoxMesh { Size = new Vector3(8, 0.05f, 6) }, Position = new Vector3(0, -0.12f, 0),
            MaterialOverride = new StandardMaterial3D { AlbedoColor = new Color(0.10f, 0.11f, 0.13f), Roughness = 1 }
        });
        var camera = new Camera3D { Position = new Vector3(4, 4.8f, 6), Current = true, Fov = 45 };
        root.AddChild(camera); camera.LookAt(new Vector3(0, 0.35f, 0), Vector3.Up);
        var canvas = new CanvasLayer(); root.AddChild(canvas);
        var label = new Label { Position = new Vector2(24, 20), Text = TranslationServer.GetLocale().StartsWith("ru") ? ru : en };
        label.AddThemeFontSizeOverride("font_size", 22); canvas.AddChild(label);
    }
}
ArrayQuadLab3D.cs
C#
using Reckfell;

public partial class ArrayQuadLab3D : Entity3D
{
    public ArrayMesh Generated { get; private set; } = null!;

    public static ArrayMesh CreateQuad()
    {
        Vector3[] vertices = [new(-1, 0, -1), new(1, 0, -1), new(1, 0, 1), new(-1, 0, 1)];
        Vector3[] normals = [Vector3.Up, Vector3.Up, Vector3.Up, Vector3.Up];
        Vector2[] uv = [new(0, 0), new(1, 0), new(1, 1), new(0, 1)];
        int[] indices = [0, 1, 2, 0, 2, 3];
        using var arrays = new Reckfell.Collections.Array();
        arrays.Resize((int)Mesh.ArrayType.Max);
        arrays[(int)Mesh.ArrayType.Vertex] = vertices;
        arrays[(int)Mesh.ArrayType.Normal] = normals;
        arrays[(int)Mesh.ArrayType.TexUV] = uv;
        arrays[(int)Mesh.ArrayType.Index] = indices;
        var mesh = new ArrayMesh();
        mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays);
        return mesh;
    }

    public override void _Ready()
    {
        GeometryStage3D.Setup(this, "ArrayMesh: 4 вершины, 2 треугольника", "ArrayMesh: 4 vertices, 2 triangles");
        Generated = CreateQuad();
        AddChild(new MeshInstance3D { Mesh = Generated, MaterialOverride = GeometryStage3D.Material() });
    }
}

Build a consistent attribute set

The outer Reckfell.Collections.Array has Mesh.ArrayType.Max slots. Its indices identify attributes, not individual vertices. Unused slots stay Nil. The example supplies typed Vector3[], Vector2[] and int[] data rather than arbitrary Variant values.

SlotExample data
Vertex4 Vector3 positions
Normal4 Vector3 normals, Up here
TexUV4 Vector2 coordinates
Index6 int indices: 0, 1, 2, 0, 2, 3

Enabled per-vertex attributes must match the vertex count. Every index must lie in 0..vertexCount−1. Triangle index counts must be divisible by 3. Normals need correct length/orientation; UV data alone does not assign a texture.

The example faces upward: (C−A).Cross(B−A) for its triangle gives Up. Winding, lighting and normal direction must agree. A normal map also needs correct tangents; see SurfaceTool for their generation.

Appending is not replacing

AddSurfaceFromArrays appends a surface. A second call does not overwrite the first. For a full update of an owned resource, prepare/validate new data, then create another ArrayMesh or clear old surfaces. Do not clear a shared resource when other objects must retain the previous geometry.

Assign a surface material or use the displaying MeshInstance3D MaterialOverride. Multiple surfaces or extra material passes may require extra draws; one Mesh does not promise one draw call.

Save the result

ResourceSaver.Save stores the completed ArrayMesh, for example user://quad.rdata; check Error. The fixture saves and reloads with CacheMode.Ignore, retaining its surface and four vertices. Decide separately whether to regenerate at startup or include a saved resource in export. Saving a mesh does not automatically save the level, camera or C# code.

Checks cover attributes, front face, AABB, append/clear behavior, save/load and real rendering. This is geometry without a physics collider; the quad does not establish skin, blend-shape or malformed-input support.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.