Skip to content

Instantly share code, notes, and snippets.

@Kiokiok
Last active January 22, 2025 01:08
Show Gist options
  • Save Kiokiok/414fa2f0d83bc22bfdf8d6c66ceef0a8 to your computer and use it in GitHub Desktop.
Save Kiokiok/414fa2f0d83bc22bfdf8d6c66ceef0a8 to your computer and use it in GitHub Desktop.
Simple example on how to render flecs entities in Godot. Two ways : individual instances or multimesh.
using Godot;
using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;
public partial class FlecsBootstrapper : Node3D
{
[Export]
Mesh renderMesh;
[Export]
int renderAmount = 1000;
[Export]
bool useMultimesh = false;
World ecsWorld;
Rid multimesh;
Rid instance;
public override void _Ready()
{
ecsWorld = World.Create();
if(useMultimesh)
{
multimesh = RenderingServer.MultimeshCreate();
instance = RenderingServer.InstanceCreate();
RenderingServer.InstanceSetBase(instance, multimesh);
RenderingServer.InstanceSetTransform(instance, Transform3D.Identity);
RenderingServer.InstanceSetScenario(instance, GetWorld3D().Scenario);
RenderingServer.MultimeshSetMesh(multimesh, renderMesh.GetRid());
RenderingServer.MultimeshAllocateData(multimesh, renderAmount, RenderingServer.MultimeshTransformFormat.Transform3D);
}
for (int i = 0; i < renderAmount; i++)
{
Vector3 randomPos = new Vector3();
randomPos.X = GD.RandRange(-150, 150);
randomPos.Y = GD.RandRange(-150, 150);
randomPos.Z = GD.RandRange(-150, 150);
CreateEntity(renderMesh, new Transform3D(Basis.Identity, randomPos), i);
}
// support for the flecs explorer
ecsWorld.Import<Ecs.Units>();
ecsWorld.Import<Ecs.Monitor>();
ecsWorld.Set(default(EcsRest));
// --- SYSTEMS AND OBSERVER
// Dummy rotation system
// Changes the entity rotation every frame
{
FilterBuilder rotationRoutineFilterBuilder = ecsWorld.FilterBuilder()
.Term<TransformComponent>();
ecsWorld.Routine(rotationRoutineFilterBuilder, callback: (Iter it) =>
{
Column<TransformComponent> tranformsCol = it.Field<TransformComponent>(1);
for (int i = 0; i < it.Count(); i++)
{
// grab the Tranform3D
Transform3D trs = tranformsCol[i].value;
trs = trs.RotatedLocal(Vector3.Up, 2 * it.DeltaTime());
tranformsCol[i].value = trs;
}
});
}
// Rendering sync system
// Update the render instance using the RenderingServer
{
FilterBuilder renderUpdateRoutineFilterBuilder = ecsWorld.FilterBuilder()
.Term<TransformComponent>()
.Term<RenderIDComponent>();
ecsWorld.Routine(renderUpdateRoutineFilterBuilder, callback: (Iter it) =>
{
Column<TransformComponent> tranformsCol = it.Field<TransformComponent>(1);
Column<RenderIDComponent> renderIDCol = it.Field<RenderIDComponent>(2);
if (useMultimesh)
{
for (int i = 0; i < it.Count(); i++)
{
Transform3D trs = tranformsCol[i].value;
int myID = renderIDCol[i].id;
RenderingServer.MultimeshInstanceSetTransform(multimesh, myID, trs);
}
}
else
{
for (int i = 0; i < it.Count(); i++)
{
Transform3D trs = tranformsCol[i].value;
Rid myInstanceID = renderIDCol[i].reference;
RenderingServer.InstanceSetTransform(myInstanceID, trs);
}
}
});
}
// Cleaning up observer
{
FilterBuilder cleanupObserverFilterBuilder = ecsWorld.FilterBuilder()
.Term<RenderIDComponent>();
ObserverBuilder cleanupObserverBuilder = ecsWorld.ObserverBuilder()
.Event(Ecs.OnRemove);
// Example of an observer that automatically cleans up Render Instances on the Rendering Server
ecsWorld.Observer(cleanupObserverFilterBuilder, cleanupObserverBuilder, callback: (Iter it) =>
{
Column<RenderIDComponent> renderIDCol = it.Field<RenderIDComponent>(1);
if (useMultimesh)
{
// TODO : Implement Multimesh instance deletion
}
else
{
for (int i = 0; i < it.Count(); i++)
{
Rid instanceRid = renderIDCol[i].reference;
RenderingServer.FreeRid(instanceRid);
}
}
});
}
}
void CreateEntity(Mesh _mesh, Transform3D _trs, int _id)
{
Entity entity = ecsWorld.Entity();
// create transform component
TransformComponent transformComponent = new TransformComponent();
transformComponent.value = _trs;
entity.Set<TransformComponent>(transformComponent);
// create the render instance and set it up
if(useMultimesh)
{
RenderingServer.MultimeshInstanceSetTransform(multimesh, _id, _trs);
RenderIDComponent renderID = new RenderIDComponent();
renderID.id = _id;
entity.Set<RenderIDComponent>(renderID);
}
else
{
Rid instance = RenderingServer.InstanceCreate();
RenderingServer.InstanceSetBase(instance, _mesh.GetRid());
RenderingServer.InstanceSetTransform(instance, transformComponent.value);
RenderingServer.InstanceSetScenario(instance, GetWorld3D().Scenario);
// set renderID component
RenderIDComponent renderID = new RenderIDComponent();
renderID.reference = instance;
renderID.id = _id;
entity.Set<RenderIDComponent>(renderID);
}
}
public override void _Process(double delta)
{
ecsWorld.Progress((float)delta);
}
}
// --- COMPONENTS
public struct TransformComponent
{
public Transform3D value;
}
// For the sake of simplicity we reuse the same component
// In a real scenerio, we'd choose either individual Instances or multimesh instances
public struct RenderIDComponent
{
public Rid reference;
public int id;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment