using System.Collections.Generic;
using System.Numerics;
using AcDream.Core.World;
namespace AcDream.App.Rendering;
///
/// Splits a frame's landblock entities into the three draw buckets the per-cell
/// needs, using the SAME precedence as
/// :
///
/// - ServerGuid != 0 (player / NPCs / items / doors) ⇒
/// — drawn unclipped (depth only). These have no ParentCellId so they MUST be tested first.
/// - ParentCellId in the visible set ⇒ [cell] — per-cell, portal-clipped.
/// - ParentCellId == null (outdoor scenery / building shell) ⇒
/// — drawn through the doorway, clipped to OutsideView.
///
/// A static whose ParentCellId is NOT in is dropped (its cell
/// isn't drawn this frame). Entities with no MeshRefs are skipped. Pure; GL-free; unit-tested.
///
public static class InteriorEntityPartition
{
public sealed class Result
{
public Dictionary> ByCell { get; } = new();
public List Outdoor { get; } = new();
public List LiveDynamic { get; } = new();
}
public static Result Partition(
HashSet visibleCells,
IEnumerable<(uint LandblockId, Vector3 AabbMin, Vector3 AabbMax,
IReadOnlyList Entities,
IReadOnlyDictionary? AnimatedById)> landblockEntries)
{
var result = new Result();
foreach (var entry in landblockEntries)
{
foreach (var e in entry.Entities)
{
if (e.MeshRefs.Count == 0) continue;
if (e.ServerGuid != 0) // live-dynamic — precedence first (no ParentCellId)
{
result.LiveDynamic.Add(e);
}
else if (e.ParentCellId is uint cell)
{
if (!visibleCells.Contains(cell)) continue; // its cell isn't drawn this frame
if (!result.ByCell.TryGetValue(cell, out var list))
result.ByCell[cell] = list = new List();
list.Add(e);
}
else // outdoor scenery / building shell
{
result.Outdoor.Add(e);
}
}
}
return result;
}
}