reckfellENGINE

SurfaceTool: attributes, normals and tangents

SetUV/SetColor/AddVertex order, smoothing, indexing and Commit without unnoticed accumulation.

On this page

Run the colored surface

Add GeometryStage3D.cs and SurfaceToolLab3D.cs. Attach SurfaceToolLab3D to a separate Entity3D-root 1.0.0 C# level, set a 640×420 viewport, build and press F6. The quad displays a vertex-color gradient with normals and tangents. Its material enables VertexColorUseAsAlbedo.

SurfaceTool with vertex colors and generated attributes

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);
    }
}
SurfaceToolLab3D.cs
C#
using Reckfell;

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

    public static ArrayMesh CreateQuad()
    {
        Vector3[] points = [new(-1, 0, -1), new(1, 0, -1), new(1, 0, 1), new(-1, 0, 1)];
        Vector2[] uv = [new(0, 0), new(1, 0), new(1, 1), new(0, 1)];
        Color[] colors = [new(0.24f, 0.44f, 1), new(0.5f, 0.65f, 1), Colors.White, new(0.24f, 0.44f, 1)];
        int[] indices = [0, 1, 2, 0, 2, 3];
        using var tool = new SurfaceTool();
        tool.Begin(Mesh.PrimitiveType.Triangles);
        tool.SetSmoothGroup(0);
        foreach (int index in indices)
        {
            tool.SetColor(colors[index]);
            tool.SetUV(uv[index]);
            tool.AddVertex(points[index]);
        }
        tool.GenerateNormals();
        tool.Index();
        tool.GenerateTangents();
        return tool.Commit();
    }

    public override void _Ready()
    {
        GeometryStage3D.Setup(this, "SurfaceTool: атрибуты до AddVertex", "SurfaceTool: attributes before AddVertex");
        Generated = CreateQuad();
        AddChild(new MeshInstance3D { Mesh = Generated, MaterialOverride = GeometryStage3D.Material(vertexColors: true) });
    }
}

Call order

  1. Begin selects the primitive type.
  2. SetColor, SetUV, SetNormal and other setters prepare the next vertex's attributes.
  3. AddVertex captures the position and current attributes.
  4. After building, GenerateNormals for Triangles when correct normals were not supplied.
  5. Index combines matching full attribute sets. GenerateTangents requires UVs, normals and triangles.
  6. Commit creates an ArrayMesh or appends to existing.

The enabled attribute format is established as vertex emission begins. Set required attributes before the first AddVertex; introducing color after several colorless vertices is invalid. Attribute values persist until changed, so a missing SetUV can silently reuse the previous UV.

Smooth and flat shading

SetSmoothGroup(0) allows smoothing within one group. Different groups separate normal calculation; uint.MaxValue disables smoothing for flat faces. Set groups before the affected vertices and generate normals after building. Both quad faces are coplanar, so their Up normals agree.

A matching position is not necessarily a matching vertex: UV, color, normal or other data may differ. Index preserves those seams. This example becomes four vertices and six indices; do not expect the same reduction for a seamed model.

Tangents describe UV orientation for tangent-space normal mapping. Generation does not replace a valid UV layout and may split data at seams. Checks cover tangent-array length and sign; arbitrary UV topology needs its own verification.

Avoid accumulating surfaces

Commit(existing) appends, as separately checked in the fixture. It does not replace previous geometry or automatically optimize per-frame rebuilding. Clear resets SurfaceTool for a new build; replace geometry with a prepared owned Mesh or explicit ArrayMesh surface management.

Checks cover attribute order, colors, normals, tangents, indexing, append behavior and actual Windows/OpenGL rendering. Collider creation, exports and measured GPU speedup are separate tasks.

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.