This repository has been archived on 2026-10-29. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
EveUnity/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityEntitySoaPresenter.cs

96 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using GameCult.Eve.Surface;
using GameCult.Mesh;
#nullable enable
namespace GameCult.Eve.UnityScene
{
public interface IEveUnityEntitySoaViewDocumentSource
{
event Action<EveEntitySoaViewDocument, ICultMeshBodyReadLease> EntityViewAvailable;
}
/// <summary>Projects portable entity SoA generations into the ordinary Unity scene sink.</summary>
public sealed class EveUnityEntitySoaPresenter
{
private readonly IEveUnityPlayableWorldSceneSink _sink;
private readonly HashSet<string> _known = new HashSet<string>(StringComparer.Ordinal);
public EveUnityEntitySoaPresenter(IEveUnityPlayableWorldSceneSink sink)
{
_sink = sink ?? throw new ArgumentNullException(nameof(sink));
}
public void Apply(EveEntitySoaViewDocument document, ICultMeshBodyReadLease lease)
{
using var view = EveUnityEntitySoaView.Open(document, lease);
var presented = new List<EveUnityPresentedEntity>();
var next = new HashSet<string>(StringComparer.Ordinal);
for (var row = 0; row < view.EntityCount; row++)
{
if (!view.TryReadInt32("entity.index", row, out var entityIndex)) continue;
if (view.TryReadByte("render.visibility", row, out var visible) && visible == 0) continue;
if (!view.TryReadVector3("transform.position", row, out var position)) continue;
view.TryReadFloat("transform.rotation.radians", row, out var rotationRadians);
view.TryReadVector3("transform.velocity", row, out var velocity);
view.TryReadFloat("physics.body.radius", row, out var radius);
view.TryReadFloat("render.scale", row, out var scale);
view.TryReadInt32("render.lod", row, out var lod);
view.TryReadUInt32("render.group.id", row, out var groupId);
var renderGroup = view.RenderGroups.FirstOrDefault(group => group.GroupId == groupId);
var identity = document.Identities.FirstOrDefault(item => item.Index == entityIndex);
var entityId = identity?.EntityId ?? $"entity:{entityIndex}";
next.Add(entityId);
var assetRef = string.IsNullOrWhiteSpace(identity?.AssetRef)
? renderGroup?.MeshAssetRef ?? ""
: identity!.AssetRef;
var scalarState = new Dictionary<string, float>(StringComparer.Ordinal);
foreach (var semantic in view.FloatSemantics)
if (view.TryReadFloat(semantic, row, out var scalar))
scalarState[semantic] = scalar;
var fact = new EveUnityPresentedEntity(
entityIndex,
entityId,
identity?.EntityKind ?? "entity",
string.IsNullOrWhiteSpace(identity?.Label) ? entityId : identity!.Label,
identity?.Faction ?? "",
position,
rotationRadians * 57.2957795f,
velocity,
radius,
scale > 0f ? scale : renderGroup?.DefaultScale ?? 1f,
groupId,
lod,
identity?.Selectable ?? true,
identity?.Controllable ?? false,
assetRef,
scalarState);
presented.Add(fact);
if (_sink is IEveUnityEntityGenerationSink)
continue;
_sink.UpsertEntity(
fact.ToPlayableWorldEntity(),
new EveUnityPlayableWorldAssetBinding(
assetRef,
"entity",
string.IsNullOrWhiteSpace(assetRef) ? "unity-generated-placeholder" : "provider-asset-ref"));
}
if (_sink is IEveUnityEntityGenerationSink generationSink)
{
generationSink.ApplyGeneration(new EveUnityPresentedEntityGeneration(
document.ViewId, document.ProducerEpoch, document.Sequence, presented));
}
else
{
foreach (var entityId in _known.Where(entityId => !next.Contains(entityId)).ToArray())
_sink.RemoveEntity(entityId);
}
_known.Clear();
foreach (var entityId in next) _known.Add(entityId);
}
}
}