RenderingServer: create an instance and release its RID
Display without MeshInstance3D, strong resource references and cleanup on tree exit.
When a server API fits
MeshInstance3D already manages a rendering instance and tree integration. RenderingServer lets you own instances directly through RIDs. This can suit a custom rendering system but adds responsibility for resources, scenario, transforms, visibility and release. Choosing a lower-level API does not establish a speedup.
Run an instance without MeshInstance3D
Add GeometryStage3D.cs and ServerBoxLab3D.cs. Attach ServerBoxLab3D to a separate Entity3D-root 1.0.0 C# level, set a 640×420 viewport, build and press F6. A blue cube appears. The fixture floor uses an ordinary MeshInstance3D; the blue cube uses RenderingServer.InstanceCreate.
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);
}
}ServerBoxLab3D.cs
using Reckfell;
public partial class ServerBoxLab3D : Entity3D
{
public bool Active => _instance.IsValid;
public Rid Instance => _instance;
private Rid _instance;
private BoxMesh? _mesh;
private StandardMaterial3D? _material;
public Error Open()
{
if (!ReckfellThread.IsMainThread()) return Error.Unavailable;
if (!IsInsideTree()) return Error.Unconfigured;
if (Active) return Error.Busy;
_material = GeometryStage3D.Material();
_mesh = new BoxMesh { Size = new Vector3(1.5f, 1.5f, 1.5f), Material = _material };
_instance = RenderingServer.InstanceCreate();
RenderingServer.InstanceSetBase(_instance, _mesh.GetRid());
RenderingServer.InstanceSetScenario(_instance, GetWorld3D().Scenario);
UpdateTransform();
SetProcess(true);
return Error.Ok;
}
private void UpdateTransform()
{
RenderingServer.InstanceSetTransform(_instance, GlobalTransform.TranslatedLocal(new Vector3(0, 0.75f, 0)));
}
public void Close()
{
if (Active) RenderingServer.FreeRid(_instance);
_instance = default;
_mesh?.Dispose(); _mesh = null;
_material?.Dispose(); _material = null;
SetProcess(false);
}
public override void _Ready()
{
GeometryStage3D.Setup(this, "RenderingServer: экземпляр без MeshInstance3D", "RenderingServer: an instance without MeshInstance3D");
Error error = Open();
if (error != Error.Ok) ReckfellApi.PrintErr("ServerBox open: " + error);
}
public override void _Process(double delta)
{
if (Active) UpdateTransform();
}
public override void _ExitTree() => Close();
}Ownership chain
- Keep strong C# Mesh and Material references; their RIDs do not replace those references.
- InstanceCreate creates an owned rendering instance.
- InstanceSetBase assigns the Mesh RID; InstanceSetScenario attaches it to GetWorld3D().Scenario.
- InstanceSetTransform supplies a world transform. It does not automatically follow an Entity; this code updates it.
- FreeRid releases the owned instance. Then release owned resources and reset the saved RID.
A RID is an identifier value, not a Resource with automatic lifetime. Copying it does not own the native object. IsValid means nonzero; a separate saved copy can remain IsValid = true after FreeRid even though the native instance is gone. Do not make later calls with that stale copy.
Do not FreeRid a borrowed World3D.Scenario or manually release a live Mesh RID in place of its resource owner. This example frees only its own instance RID; Material and Mesh are retained until then and released through their C# wrappers.
Exit and reentry
Close is idempotent and clears RID/references. _ExitTree calls it. Readding the same Entity does not automatically repeat _Ready; call Open explicitly after attaching it when a new instance is needed. Checks cover reopening with a new RID, tree exit and no double release. The render test verifies that Close actually removes the cube from viewport pixels, not merely changes Active.
Run Open/Close and Entity management on the main thread in this architecture. Server API threading does not grant blanket access to trees/shared resources; see thread safety.
PhysicsServer2D/3D and other servers have their own RID types/contracts: bodies, shapes, spaces, callbacks and simulation steps are not created by this rendering example. It does not implement ECS, physics, individual gameplay scripts, replication or streaming. For many identical meshes, consider the simpler MultiMesh first.