SurfaceTool: attributes, normals and tangents
SetUV/SetColor/AddVertex order, smoothing, indexing and Commit without unnoticed accumulation.
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.
GeometryStage3D.cs
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
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
- Begin selects the primitive type.
- SetColor, SetUV, SetNormal and other setters prepare the next vertex's attributes.
- AddVertex captures the position and current attributes.
- After building, GenerateNormals for Triangles when correct normals were not supplied.
- Index combines matching full attribute sets. GenerateTangents requires UVs, normals and triangles.
- 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.