ImmediateMesh: small frequently updated geometry
A 12-line wireframe, SurfaceBegin/End and rebuilding without surface accumulation.
Run the wireframe
Add GeometryStage3D.cs and ImmediateWireLab3D.cs. Attach ImmediateWireLab3D to a separate Entity3D-root 1.0.0 C# level, set a 640×420 viewport, build and press F6. A 12-line box wireframe appears; Animate changes its height. Disable Animate for a static result.
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);
}
}ImmediateWireLab3D.cs
using System;
using Reckfell;
public partial class ImmediateWireLab3D : Entity3D
{
[Export] public bool Animate { get; set; } = true;
public ImmediateMesh Generated { get; private set; } = null!;
private StandardMaterial3D _material = null!;
private double _time;
public override void _Ready()
{
GeometryStage3D.Setup(this, "ImmediateMesh: 12 отрезков, 1 поверхность", "ImmediateMesh: 12 lines, 1 surface");
_material = GeometryStage3D.Material(unshaded: true);
Generated = new ImmediateMesh();
AddChild(new MeshInstance3D { Mesh = Generated });
Rebuild(1.4f);
}
public Error Rebuild(float height)
{
if (Generated is null) return Error.Unconfigured;
if (!float.IsFinite(height) || height <= 0 || height > 3) return Error.InvalidParameter;
Vector3[] points = [new(-1, 0, -1), new(1, 0, -1), new(1, 0, 1), new(-1, 0, 1),
new(-1, height, -1), new(1, height, -1), new(1, height, 1), new(-1, height, 1)];
int[] edges = [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7];
Generated.ClearSurfaces();
Generated.SurfaceBegin(Mesh.PrimitiveType.Lines, _material);
foreach (int index in edges) Generated.SurfaceAddVertex(points[index]);
Generated.SurfaceEnd();
return Error.Ok;
}
public override void _Process(double delta)
{
if (!Animate) return;
_time += delta;
Rebuild(1.4f + 0.3f * (float)Math.Sin(_time));
}
}Build lifetime
SurfaceBegin starts one surface of the chosen primitive type. For Lines, every two SurfaceAddVertex calls define a line; 12 lines require 24 emitted vertices. SurfaceEnd finishes it. Do not emit before SurfaceBegin or leave a build unfinished.
SurfaceSetColor, SurfaceSetNormal and UV setters affect subsequent vertices. This example uses a surface material for color. Unshaded makes diagnostic lines independent of lighting/normals used by ordinary surfaces.
Rebuild without accumulation
A surface does not disappear automatically on the next frame. The code calls ClearSurfaces before rebuilding the owned wireframe. Repeating SurfaceBegin/End without clearing appends another surface. The fixture retains exactly one after 20 rebuilds.
Rebuild validates height and resource readiness before clearing. Copying an ImmediateMesh reference does not create independent geometry: clearing a shared resource affects every display. For large static models, use ArrayMesh/SurfaceTool rather than emitting thousands of unchanged triangles each frame.
Scope
The wireframe displays geometry, not a physics shape. It does not collide or create navigation data. Line thickness/antialiasing may depend on renderer; checks cover visible output on the installed Windows/OpenGL build. ImmediateMesh does not expose every ArrayMesh method in C#: the fixture reads positions through SurfaceGetArrays, not an invented ImmediateMesh.SurfaceGetArrayLen.
For selective large-mesh updates, investigate the exact buffer format/update API separately. Full rebuilds here handle 24 positions; that is not an arbitrary-size performance promise.