2024-10-13 00:08:01 +02:00
using MoonTools.ECS ;
using Nerfed.Runtime.Components ;
using Nerfed.Runtime.Util ;
using System.Numerics ;
2024-10-13 01:05:46 +02:00
// TODO:
// Explore if having a WorldTransform and LocalTransfom component each holding position, rotation, scale values and the matricies is useful.
// Often you need to either get or set these values.
// If so, we probably need a utility funciton to do so. Since changing these values means that we need to update all the related data + children as well.
// TODO:
// When modifying transform all the children need to be updated as well.
2024-10-13 00:08:01 +02:00
namespace Nerfed.Runtime.Systems
{
2024-10-13 01:05:46 +02:00
public class LocalToWorldSystem : MoonTools . ECS . System
2024-10-13 00:08:01 +02:00
{
2024-10-20 23:17:41 +02:00
private readonly bool useParallelFor = true ; // When having a low amount of transforms or when in debug mode this might be slower.
2024-10-13 00:08:01 +02:00
private readonly Filter rootEntitiesFilter ;
2024-10-21 22:48:57 +02:00
private readonly Filter entitiesWithoutLocalToWorldFilter ;
2024-10-20 03:51:59 +02:00
private readonly Action < int > updateWorldTransform ;
2024-10-13 00:08:01 +02:00
2024-10-13 01:05:46 +02:00
public LocalToWorldSystem ( World world ) : base ( world )
2024-10-13 00:08:01 +02:00
{
2024-10-15 00:41:45 +02:00
rootEntitiesFilter = FilterBuilder . Include < LocalTransform > ( ) . Exclude < Child > ( ) . Build ( ) ;
2024-10-20 03:51:59 +02:00
if ( useParallelFor )
{
2024-10-21 22:48:57 +02:00
entitiesWithoutLocalToWorldFilter = FilterBuilder . Include < LocalTransform > ( ) . Exclude < LocalToWorld > ( ) . Build ( ) ;
2024-10-20 03:51:59 +02:00
updateWorldTransform = UpdateWorldTransformByIndex ;
}
2024-10-13 00:08:01 +02:00
}
public override void Update ( TimeSpan delta )
{
2024-10-20 03:51:59 +02:00
if ( rootEntitiesFilter . Empty )
{
return ;
}
if ( useParallelFor )
{
Profiler . BeginSample ( "ParallelFor.LocalToWorldCheck" ) ;
// This check is needed because some entities might not have a LocalToWorld component yet.
// Adding this during the loop will break.
2024-10-21 22:48:57 +02:00
foreach ( Entity entity in entitiesWithoutLocalToWorldFilter . Entities ) {
2024-10-20 03:51:59 +02:00
Set ( entity , new LocalToWorld ( Matrix4x4 . Identity ) ) ;
}
Profiler . EndSample ( ) ;
Profiler . BeginSample ( "ParallelFor.LocalToWorldUpdate" ) ;
// This should only be used when the filter doesn't change by executing these functions!
// So no entity deletion or setting/removing of components used by the filters in this loop.
Parallel . For ( 0 , rootEntitiesFilter . Count , updateWorldTransform ) ;
Profiler . EndSample ( ) ;
}
else
2024-10-13 00:08:01 +02:00
{
2024-10-20 03:51:59 +02:00
foreach ( Entity entity in rootEntitiesFilter . Entities )
{
2024-10-20 23:17:41 +02:00
Profiler . BeginSample ( "UpdateWorldTransform" ) ;
2024-10-20 03:51:59 +02:00
UpdateWorldTransform ( entity , Matrix4x4 . Identity ) ;
2024-10-20 23:17:41 +02:00
Profiler . EndSample ( ) ;
2024-10-20 03:51:59 +02:00
}
2024-10-13 00:08:01 +02:00
}
}
2024-10-20 03:51:59 +02:00
private void UpdateWorldTransformByIndex ( int entityFilterIndex )
{
2024-10-20 23:17:41 +02:00
Profiler . BeginSample ( "UpdateWorldTransformByIndex" ) ;
2024-10-20 03:51:59 +02:00
Entity entity = rootEntitiesFilter . NthEntity ( entityFilterIndex ) ;
UpdateWorldTransform ( entity , Matrix4x4 . Identity ) ;
2024-10-20 23:17:41 +02:00
Profiler . EndSample ( ) ;
2024-10-20 03:51:59 +02:00
}
2024-10-15 00:41:45 +02:00
private void UpdateWorldTransform ( in Entity entity , Matrix4x4 localToWorldMatrix )
2024-10-13 00:08:01 +02:00
{
// TODO: Only update dirty transforms.
2024-10-13 01:05:46 +02:00
// If a parent is dirty all the children need to update their localToWorld matrix.
// How do we check if something is dirty? How do we know if a LocalTransform has been changed?
2024-10-15 00:41:45 +02:00
if ( Has < LocalTransform > ( entity ) )
{
LocalTransform localTransform = Get < LocalTransform > ( entity ) ;
localToWorldMatrix = Matrix4x4 . Multiply ( localToWorldMatrix , localTransform . TRS ( ) ) ;
LocalToWorld localToWorld = new ( localToWorldMatrix ) ;
Set ( entity , localToWorld ) ;
}
2024-10-13 00:08:01 +02:00
2024-10-19 23:41:05 +02:00
ReverseSpanEnumerator < Entity > childEntities = World . InRelations < ChildParentRelation > ( entity ) ;
2024-10-13 00:08:01 +02:00
foreach ( Entity childEntity in childEntities )
{
UpdateWorldTransform ( childEntity , localToWorldMatrix ) ;
}
}
}
}