using Ghost.Entities;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Engine.Core;
///
/// Represents a runtime scene - a collection of entities with the same SceneID.
///
public readonly struct Scene : IEquatable
{
private readonly short _id;
///
/// Gets the unique identifier of this scene.
///
public short ID => _id;
///
/// Gets whether this scene is valid.
///
public bool IsValid => _id >= 0;
///
/// Gets an invalid scene instance.
///
public static Scene Invalid => new(-1);
internal Scene(short id)
{
_id = id;
}
public bool Equals(Scene other)
{
return _id == other._id;
}
public override bool Equals(object? obj)
{
return obj is Scene other && Equals(other);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
public static bool operator ==(Scene left, Scene right)
{
return left.Equals(right);
}
public static bool operator !=(Scene left, Scene right)
{
return !left.Equals(right);
}
public override string ToString()
{
return $"Scene(ID: {_id})";
}
}
///
/// Manages scenes within a world.
///
///
/// This is a minimal runtime representation. All metadata (like scene names)
/// should be stored in editor-only classes (SceneNode).
///
public static class SceneManager
{
private static short s_nextSceneID;
private static readonly Queue s_recycledSceneIDs = new();
///
/// Creates a new scene in the world.
///
/// The created scene.
public static Scene CreateScene()
{
if (!s_recycledSceneIDs.TryDequeue(out var id))
{
id = s_nextSceneID++;
}
return new Scene(id);
}
///
/// Destroys all entities belonging to the specified scene.
///
/// The scene to unload.
/// The world containing the entities.
public static void UnloadScene(Scene scene, World world)
{
var queryID = new QueryBuilder().WithAll().Build(world);
ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID);
using var scope = AllocationManager.CreateStackScope();
var entitiesToDestroy = new UnsafeList(128, scope.AllocationHandle);
// Iterate through all matching entities
foreach (var chunk in query.GetChunkIterator())
{
var entities = chunk.GetEntities();
var sceneIDs = chunk.GetComponentData();
for (var i = 0; i < chunk.Count; i++)
{
if (sceneIDs[i].scene.ID == scene.ID)
{
entitiesToDestroy.Add(entities[i]);
}
}
}
world.EntityManager.DestroyEntities(entitiesToDestroy.AsSpan());
s_recycledSceneIDs.Enqueue(scene.ID);
}
///
/// Gets all entities belonging to the specified scene.
///
/// The scene to query.
/// The world containing the entities.
/// Span to store the entities.
/// The number of entities written to the span.
public static UnsafeList GetSceneEntities(Scene scene, World world, AllocationHandle handle)
{
var queryID = new QueryBuilder().WithAll().Build(world);
ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID);
var entities = new UnsafeList(128, handle);
// Iterate through all matching entities
foreach (var chunk in query.GetChunkIterator())
{
var chunkEntities = chunk.GetEntities();
var sceneIDs = chunk.GetComponentData();
for (var i = 0; i < chunk.Count; i++)
{
if (sceneIDs[i].scene.ID == scene.ID)
{
entities.Add(chunkEntities[i]);
}
}
}
return entities;
}
}