Skip to content

Instantly share code, notes, and snippets.

@PumpkinPaul
Created August 14, 2025 20:33
Show Gist options
  • Save PumpkinPaul/2e418da8556dfdbef93fb5fc98bed606 to your computer and use it in GitHub Desktop.
Save PumpkinPaul/2e418da8556dfdbef93fb5fc98bed606 to your computer and use it in GitHub Desktop.
using MoonTools.ECS;
using Pathogen.Game.ECS.Components;
using System.Numerics;
using ECSSystem = MoonTools.ECS.System;
namespace Pathogen.Game.ECS.Systems;
/// <summary>
/// Sets the TransformMatrixComponent component for entities in the world.
/// </summary>
public sealed class TransformSystem : ECSSystem
{
readonly Filter _filter;
public TransformSystem(
World world
) : base(world)
{
_filter = FilterBuilder
.Include<TransformComponent>()
.Include<TransformMatrixComponent>()
.Build();
}
public override void Update(TimeSpan elapsedTime)
{
// Consider the following scene hierarchy...
// A
// |__ B
// |__ C
// |__ D
// E
// |__ F
// G
// |__ H
// All entities, A to H will be present in the filter and we process them in that order (A, B, C, D, E, F, G, H)
// Normal foreach iteration starts with entities added to the world last (H) going towards entities added to the world first (A).
// As we need to ensure parent transforms are updated before child transforms, we use an old Skool for loop.
// A is a root entity so there will be NO InRelation, the TransformMatrixComponent will be the result of A's TransformComponent.
// Next we process B, it DOES have an InRelation to a parent (A) so the TransformMatrixComponent will be the result of
// B's TramsformComponent multipled by A's TransformMatrixComponent (in that order).
// Next we process C, it DOES have an InRelation to a parent (B) so the TransformMatrixComponent will be the result of
// C's TransformComponent multipled by B's TransformMatrixComponent (in that order).
// ...and so on...
var count = _filter.Count;
for (var i = 0; i < count; i++)
{
var entity = _filter.NthEntity(i);
ref readonly var transform = ref Get<TransformComponent>(entity);
var localMatrix = Matrix4x4.CreateScale(transform.Scale) * Matrix4x4.CreateFromQuaternion(transform.Rotation) * Matrix4x4.CreateTranslation(transform.Position);
if (HasInRelation<TransformParentChildRelation>(entity) == false)
{
Set(entity, new TransformMatrixComponent
{
Value = localMatrix
});
continue;
}
// Children can only have one transform parent so get the singleton / first
var parent = InRelationSingleton<TransformParentChildRelation>(entity);
ref readonly var parentTransformMatrix = ref Get<TransformMatrixComponent>(parent);
Set(entity, new TransformMatrixComponent
{
Value = localMatrix * parentTransformMatrix.Value
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment