implement Filter
parent
272fd6b492
commit
b3ff7e3f1c
|
@ -13,13 +13,12 @@ namespace MoonTools.ECS.Rev2
|
|||
new Dictionary<ComponentId, int>();
|
||||
public SortedDictionary<ComponentId, ArchetypeEdge> Edges = new SortedDictionary<ComponentId, ArchetypeEdge>();
|
||||
|
||||
public int Count;
|
||||
public int Count => RowToEntity.Count;
|
||||
|
||||
public Archetype(ArchetypeId id, ArchetypeSignature signature)
|
||||
{
|
||||
Id = id;
|
||||
Signature = signature;
|
||||
Count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,161 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MoonTools.ECS.Rev2
|
||||
{
|
||||
// TODO: do we want to get fancy with queries beyond Include and Exclude?
|
||||
// TODO: need an edge iterator as part of this nested horseshit
|
||||
public class Filter
|
||||
{
|
||||
private Archetype Start;
|
||||
private Archetype EmptyArchetype;
|
||||
private HashSet<ComponentId> Included;
|
||||
private HashSet<ComponentId> Excluded;
|
||||
|
||||
public EntityEnumerator Entities => new EntityEnumerator(Start);
|
||||
public EntityEnumerator Entities => new EntityEnumerator(this);
|
||||
internal ArchetypeEnumerator Archetypes => new ArchetypeEnumerator(this);
|
||||
public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this);
|
||||
|
||||
private ref struct FilterEnumerator
|
||||
public bool Empty
|
||||
{
|
||||
get
|
||||
{
|
||||
var empty = true;
|
||||
|
||||
foreach (var archetype in Archetypes)
|
||||
{
|
||||
if (archetype.Count > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
foreach (var archetype in Archetypes)
|
||||
{
|
||||
count += archetype.Count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: this WILL crash if the index is out of range!
|
||||
public EntityId NthEntity(int index)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
foreach (var archetype in Archetypes)
|
||||
{
|
||||
count += archetype.Count;
|
||||
if (index < count)
|
||||
{
|
||||
return archetype.RowToEntity[index];
|
||||
}
|
||||
|
||||
index -= count;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Filter index out of range!");
|
||||
}
|
||||
|
||||
public EntityId RandomEntity
|
||||
{
|
||||
get
|
||||
{
|
||||
var randomIndex = RandomManager.Next(Count);
|
||||
return NthEntity(randomIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public Filter(Archetype emptyArchetype, HashSet<ComponentId> included, HashSet<ComponentId> excluded)
|
||||
{
|
||||
EmptyArchetype = emptyArchetype;
|
||||
Included = included;
|
||||
Excluded = excluded;
|
||||
}
|
||||
|
||||
internal ref struct ArchetypeEnumerator
|
||||
{
|
||||
private Archetype CurrentArchetype;
|
||||
private bool Active;
|
||||
|
||||
public FilterEnumerator(Archetype start)
|
||||
// TODO: pool these
|
||||
private Queue<Archetype> ArchetypeQueue = new Queue<Archetype>();
|
||||
private Queue<Archetype> ArchetypeSearchQueue = new Queue<Archetype>();
|
||||
private HashSet<Archetype> Explored = new HashSet<Archetype>();
|
||||
|
||||
public ArchetypeEnumerator GetEnumerator() => this;
|
||||
|
||||
public ArchetypeEnumerator(Filter filter)
|
||||
{
|
||||
CurrentArchetype = start;
|
||||
Active = false;
|
||||
|
||||
var empty = filter.EmptyArchetype;
|
||||
ArchetypeSearchQueue.Enqueue(empty);
|
||||
|
||||
while (ArchetypeSearchQueue.TryDequeue(out var current))
|
||||
{
|
||||
// exclude the empty archetype
|
||||
var satisfiesFilter = filter.Included.Count != 0;
|
||||
|
||||
foreach (var componentId in filter.Included)
|
||||
{
|
||||
if (!current.ComponentToColumnIndex.ContainsKey(componentId))
|
||||
{
|
||||
satisfiesFilter = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var componentId in filter.Excluded)
|
||||
{
|
||||
if (current.ComponentToColumnIndex.ContainsKey(componentId))
|
||||
{
|
||||
satisfiesFilter = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (satisfiesFilter)
|
||||
{
|
||||
ArchetypeQueue.Enqueue(current);
|
||||
}
|
||||
|
||||
// if the current archetype satisfies the filter, we need to add all edges that
|
||||
// do not have an excluded component
|
||||
// if the current archetype does not satisfy the filter, we need to add all edges that
|
||||
// include an included component
|
||||
foreach (var (componentId, edge) in current.Edges)
|
||||
{
|
||||
if (satisfiesFilter)
|
||||
{
|
||||
if (!filter.Excluded.Contains(componentId) && !Explored.Contains(edge.Add))
|
||||
{
|
||||
Explored.Add(edge.Add);
|
||||
ArchetypeSearchQueue.Enqueue(edge.Add);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter.Included.Contains(componentId) && !Explored.Contains(edge.Add))
|
||||
{
|
||||
Explored.Add(edge.Add);
|
||||
ArchetypeSearchQueue.Enqueue(edge.Add);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (!Active)
|
||||
{
|
||||
Active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: go to next available edge
|
||||
return ArchetypeQueue.TryDequeue(out CurrentArchetype!);
|
||||
}
|
||||
|
||||
public Archetype Current => CurrentArchetype;
|
||||
|
@ -38,39 +163,50 @@ namespace MoonTools.ECS.Rev2
|
|||
|
||||
public ref struct EntityEnumerator
|
||||
{
|
||||
private FilterEnumerator FilterEnumerator;
|
||||
private ReverseSpanEnumerator<EntityId> EntityListEnumerator;
|
||||
private bool EntityListEnumeratorActive;
|
||||
private EntityId CurrentEntity;
|
||||
|
||||
public EntityEnumerator GetEnumerator() => this;
|
||||
|
||||
public EntityEnumerator(Archetype start)
|
||||
// TODO: pool this
|
||||
Queue<EntityId> EntityQueue = new Queue<EntityId>();
|
||||
|
||||
internal EntityEnumerator(Filter filter)
|
||||
{
|
||||
FilterEnumerator = new FilterEnumerator(start);
|
||||
var archetypeEnumerator = new ArchetypeEnumerator(filter);
|
||||
|
||||
foreach (var archetype in archetypeEnumerator)
|
||||
{
|
||||
foreach (var entity in archetype.RowToEntity)
|
||||
{
|
||||
EntityQueue.Enqueue(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (!EntityListEnumeratorActive || !EntityListEnumerator.MoveNext())
|
||||
{
|
||||
if (!FilterEnumerator.MoveNext())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FilterEnumerator.Current.RowToEntity.Count != 0)
|
||||
{
|
||||
EntityListEnumerator = new ReverseSpanEnumerator<EntityId>(CollectionsMarshal.AsSpan(FilterEnumerator.Current.RowToEntity));
|
||||
EntityListEnumeratorActive = true;
|
||||
}
|
||||
|
||||
return MoveNext();
|
||||
}
|
||||
|
||||
return true;
|
||||
return EntityQueue.TryDequeue(out CurrentEntity);
|
||||
}
|
||||
|
||||
public EntityId Current => EntityListEnumerator.Current;
|
||||
public EntityId Current => CurrentEntity;
|
||||
}
|
||||
|
||||
public ref struct RandomEntityEnumerator
|
||||
{
|
||||
private Filter Filter;
|
||||
private LinearCongruentialEnumerator LinearCongruentialEnumerator;
|
||||
|
||||
public RandomEntityEnumerator GetEnumerator() => this;
|
||||
|
||||
internal RandomEntityEnumerator(Filter filter)
|
||||
{
|
||||
Filter = filter;
|
||||
LinearCongruentialEnumerator =
|
||||
RandomManager.LinearCongruentialSequence(filter.Count);
|
||||
}
|
||||
|
||||
public bool MoveNext() => LinearCongruentialEnumerator.MoveNext();
|
||||
public EntityId Current => Filter.NthEntity(LinearCongruentialEnumerator.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace MoonTools.ECS.Rev2
|
|||
IdAssigner<EntityId> EntityIdAssigner = new IdAssigner<EntityId>();
|
||||
IdAssigner<ComponentId> ComponentIdAssigner = new IdAssigner<ComponentId>();
|
||||
|
||||
public readonly Archetype EmptyArchetype;
|
||||
|
||||
private bool IsDisposed;
|
||||
|
||||
public delegate void RefAction<T1, T2>(ref T1 arg1, ref T2 arg2);
|
||||
|
@ -34,7 +36,7 @@ namespace MoonTools.ECS.Rev2
|
|||
public World()
|
||||
{
|
||||
// Create the Empty Archetype
|
||||
CreateArchetype(ArchetypeSignature.Empty);
|
||||
EmptyArchetype = CreateArchetype(ArchetypeSignature.Empty);
|
||||
}
|
||||
|
||||
private Archetype CreateArchetype(ArchetypeSignature signature)
|
||||
|
@ -62,7 +64,7 @@ namespace MoonTools.ECS.Rev2
|
|||
{
|
||||
var entityId = EntityIdAssigner.Assign();
|
||||
var emptyArchetype = ArchetypeIndex[ArchetypeSignature.Empty];
|
||||
EntityIndex.Add(entityId, new Record(emptyArchetype, 0));
|
||||
EntityIndex.Add(entityId, new Record(emptyArchetype, emptyArchetype.Count));
|
||||
emptyArchetype.RowToEntity.Add(entityId);
|
||||
return entityId;
|
||||
}
|
||||
|
@ -210,14 +212,15 @@ namespace MoonTools.ECS.Rev2
|
|||
archetype.Components[i].Delete(row);
|
||||
}
|
||||
|
||||
if (archetype.Count > 1)
|
||||
if (row != archetype.Count - 1)
|
||||
{
|
||||
// update row to entity lookup on archetype
|
||||
archetype.RowToEntity[row] = archetype.RowToEntity[archetype.Count - 1];
|
||||
archetype.RowToEntity.RemoveAt(archetype.Count - 1);
|
||||
// move last row entity to open spot
|
||||
var lastRowEntity = archetype.RowToEntity[archetype.Count - 1];
|
||||
archetype.RowToEntity[row] = lastRowEntity;
|
||||
EntityIndex[lastRowEntity] = new Record(archetype, row);
|
||||
}
|
||||
|
||||
archetype.Count -= 1;
|
||||
archetype.RowToEntity.RemoveAt(archetype.Count - 1);
|
||||
EntityIndex.Remove(entityId);
|
||||
EntityIdAssigner.Unassign(entityId);
|
||||
}
|
||||
|
@ -236,20 +239,19 @@ namespace MoonTools.ECS.Rev2
|
|||
from.Components[i].Delete(row);
|
||||
}
|
||||
|
||||
if (from.Count > 1)
|
||||
if (row != from.Count - 1)
|
||||
{
|
||||
// update row to entity lookup on from archetype
|
||||
from.RowToEntity[row] = from.RowToEntity[from.Count - 1];
|
||||
from.RowToEntity.RemoveAt(from.Count - 1);
|
||||
EntityIndex[from.RowToEntity[row]] = new Record(from, row);
|
||||
// move last row entity to open spot
|
||||
var lastRowEntity = from.RowToEntity[from.Count - 1];
|
||||
from.RowToEntity[row] = lastRowEntity;
|
||||
EntityIndex[lastRowEntity] = new Record(from, row);
|
||||
}
|
||||
|
||||
from.RowToEntity.RemoveAt(from.Count - 1);
|
||||
|
||||
// update row to entity lookup on to archetype
|
||||
EntityIndex[entityId] = new Record(to, to.Count);
|
||||
to.RowToEntity.Add(entityId);
|
||||
|
||||
to.Count += 1;
|
||||
from.Count -= 1;
|
||||
}
|
||||
|
||||
private void MoveEntityToLowerArchetype(EntityId entityId, int row, Archetype from, Archetype to, ComponentId removed)
|
||||
|
@ -269,101 +271,60 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
}
|
||||
|
||||
if (from.Count > 1)
|
||||
if (row != from.Count - 1)
|
||||
{
|
||||
// update row to entity lookup on from archetype
|
||||
from.RowToEntity[row] = from.RowToEntity[from.Count - 1];
|
||||
from.RowToEntity.RemoveAt(from.Count - 1);
|
||||
EntityIndex[from.RowToEntity[row]] = new Record(from, row);
|
||||
var lastRowEntity = from.RowToEntity[from.Count - 1];
|
||||
from.RowToEntity[row] = lastRowEntity;
|
||||
EntityIndex[lastRowEntity] = new Record(from, row);
|
||||
}
|
||||
|
||||
from.RowToEntity.RemoveAt(from.Count - 1);
|
||||
|
||||
// update row to entity lookup on to archetype
|
||||
EntityIndex[entityId] = new Record(to, to.Count);
|
||||
to.RowToEntity.Add(entityId);
|
||||
|
||||
to.Count += 1;
|
||||
from.Count -= 1;
|
||||
}
|
||||
|
||||
public unsafe void ForEachEntity<T, T1, T2>(ArchetypeSignature signature,
|
||||
public unsafe void ForEachEntity<T, T1, T2>(Filter filter,
|
||||
T rowForEachContainer) where T : IForEach<T1, T2> where T1 : unmanaged where T2 : unmanaged
|
||||
{
|
||||
var archetype = ArchetypeIndex[signature];
|
||||
|
||||
var componentIdOne = signature[0];
|
||||
var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne];
|
||||
var columnOneElements = archetype.Components[columnIndexOne].Elements;
|
||||
|
||||
var componentIdTwo = signature[1];
|
||||
var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo];
|
||||
var columnTwoElements = archetype.Components[columnIndexTwo].Elements;
|
||||
|
||||
for (int i = archetype.Count - 1; i >= 0; i -= 1)
|
||||
foreach (var archetype in filter.Archetypes)
|
||||
{
|
||||
rowForEachContainer.Update(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]);
|
||||
}
|
||||
var componentIdOne = archetype.Signature[0];
|
||||
var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne];
|
||||
var columnOneElements = archetype.Components[columnIndexOne].Elements;
|
||||
|
||||
foreach (var edge in archetype.Edges.Values)
|
||||
{
|
||||
if (edge.Add != archetype)
|
||||
var componentIdTwo = archetype.Signature[1];
|
||||
var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo];
|
||||
var columnTwoElements = archetype.Components[columnIndexTwo].Elements;
|
||||
|
||||
for (int i = archetype.Count - 1; i >= 0; i -= 1)
|
||||
{
|
||||
ForEachEntity<T, T1, T2>(edge.Add.Signature, rowForEachContainer);
|
||||
rowForEachContainer.Update(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void ForEachEntity<T1, T2>(ArchetypeSignature signature, RefAction<T1, T2> rowAction) where T1 : unmanaged where T2 : unmanaged
|
||||
public unsafe void ForEachEntity<T1, T2>(Filter filter, RefAction<T1, T2> rowAction) where T1 : unmanaged where T2 : unmanaged
|
||||
{
|
||||
var archetype = ArchetypeIndex[signature];
|
||||
|
||||
var componentIdOne = signature[0];
|
||||
var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne];
|
||||
var columnOneElements = archetype.Components[columnIndexOne].Elements;
|
||||
|
||||
var componentIdTwo = signature[1];
|
||||
var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo];
|
||||
var columnTwoElements = archetype.Components[columnIndexTwo].Elements;
|
||||
|
||||
for (int i = archetype.Count - 1; i >= 0; i -= 1)
|
||||
foreach (var archetype in filter.Archetypes)
|
||||
{
|
||||
rowAction(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]);
|
||||
}
|
||||
var componentIdOne = archetype.Signature[0];
|
||||
var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne];
|
||||
var columnOneElements = archetype.Components[columnIndexOne].Elements;
|
||||
|
||||
foreach (var edge in archetype.Edges.Values)
|
||||
{
|
||||
if (edge.Add != archetype)
|
||||
var componentIdTwo = archetype.Signature[1];
|
||||
var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo];
|
||||
var columnTwoElements = archetype.Components[columnIndexTwo].Elements;
|
||||
|
||||
for (int i = archetype.Count - 1; i >= 0; i -= 1)
|
||||
{
|
||||
ForEachEntity(edge.Add.Signature, rowAction);
|
||||
rowAction(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ForEachEntity(ArchetypeSignature signature, Action<EntityId> rowAction)
|
||||
{
|
||||
var archetype = ArchetypeIndex[signature];
|
||||
|
||||
for (int i = 0; i < archetype.Count; i += 1)
|
||||
{
|
||||
rowAction(archetype.RowToEntity[i]);
|
||||
}
|
||||
|
||||
// recursion might get too hairy here
|
||||
foreach (var edge in archetype.Edges.Values)
|
||||
{
|
||||
if (edge.Add != archetype)
|
||||
{
|
||||
ForEachEntity(edge.Add.Signature, rowAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReverseSpanEnumerator<EntityId> Entities(ArchetypeSignature signature)
|
||||
{
|
||||
var archetype = ArchetypeIndex[signature];
|
||||
return new ReverseSpanEnumerator<EntityId>(
|
||||
CollectionsMarshal.AsSpan(archetype.RowToEntity));
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
|
|
Loading…
Reference in New Issue