feat(vfx): bind effects to live animated poses

This commit is contained in:
Erik 2026-07-14 10:56:01 +02:00
parent 96ddfdf175
commit 542dcfc384
41 changed files with 3246 additions and 741 deletions

View file

@ -148,6 +148,7 @@ public sealed class LiveEntityRuntime
private readonly Dictionary<uint, uint> _guidByLocalId = new();
private readonly Dictionary<uint, ILiveEntityAnimationRuntime> _animationsByLocalId = new();
private readonly Dictionary<uint, ILiveEntityRemoteMotionRuntime> _remoteMotionByGuid = new();
private uint _rebucketingGuid;
private uint _nextLocalEntityId;
public LiveEntityRuntime(
@ -191,6 +192,12 @@ public sealed class LiveEntityRuntime
public IReadOnlyDictionary<uint, ILiveEntityRemoteMotionRuntime> RemoteMotionRuntimes => _remoteMotionByGuid;
public ParentAttachmentState ParentAttachments { get; } = new();
/// <summary>
/// Raised after a materialized projection enters or leaves the currently
/// loaded spatial view. Logical resources remain owned by the record.
/// </summary>
public event Action<LiveEntityRecord, bool>? ProjectionVisibilityChanged;
public LiveEntityRegistrationResult RegisterLiveEntity(WorldSession.EntitySpawn incoming)
{
InboundCreateResult result = _inbound.AcceptCreate(incoming);
@ -311,7 +318,21 @@ public sealed class LiveEntityRuntime
|| record.WorldEntity is not { } entity)
return false;
_spatial.RebucketLiveEntity(entity, spatialCellOrLandblockId);
bool wasProjected = record.IsSpatiallyProjected;
bool wasVisible = record.IsSpatiallyVisible;
// GpuWorldState reports an intermediate false/true pair while moving
// between two loaded buckets. Suppress those implementation details
// and publish only the final logical visibility edge.
record.IsSpatiallyProjected = true;
_rebucketingGuid = serverGuid;
try
{
_spatial.RebucketLiveEntity(entity, spatialCellOrLandblockId);
}
finally
{
_rebucketingGuid = 0;
}
bool visible = _spatial.IsLiveEntityVisible(serverGuid);
record.IsSpatiallyVisible = visible;
if (record.ProjectionKind is LiveEntityProjectionKind.World)
@ -327,7 +348,8 @@ public sealed class LiveEntityRuntime
record.CanonicalLandblockId = spatialCellOrLandblockId == 0
? 0u
: (spatialCellOrLandblockId & 0xFFFF0000u) | 0xFFFFu;
record.IsSpatiallyProjected = true;
if (!wasProjected || wasVisible != visible)
ProjectionVisibilityChanged?.Invoke(record, visible);
return true;
}
@ -708,6 +730,8 @@ public sealed class LiveEntityRuntime
_visibleWorldEntitiesByGuid[serverGuid] = entity;
else
_visibleWorldEntitiesByGuid.Remove(serverGuid);
if (_rebucketingGuid != serverGuid)
ProjectionVisibilityChanged?.Invoke(record, visible);
}
private void TearDownRecord(LiveEntityRecord record)