feat(rendering): journal live scene projections

Mirror exact live-root and equipped-child lifetimes behind the non-drawing render-scene boundary. Ready, visibility, final-pose, rebucket, removal, and resource teardown edges retain canonical LiveEntityRuntime identity while active-only synchronization avoids resident-static scans.

Co-authored-by: Erik Nilsson <erikn@users.noreply.github.com>
This commit is contained in:
Erik 2026-07-24 22:05:42 +02:00
parent 5d19c56d15
commit 58e7c2eb99
10 changed files with 949 additions and 148 deletions

View file

@ -2,6 +2,7 @@ using System.Numerics;
using AcDream.App.Input;
using AcDream.App.Interaction;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Scene;
using AcDream.App.Rendering.Vfx;
using AcDream.App.Settings;
using AcDream.App.World;
@ -136,6 +137,7 @@ internal sealed class LiveObjectFrameController : ILiveObjectFramePhase
_animatedEntities;
private readonly EquippedChildRenderController _equippedChildren;
private readonly LiveEffectFrameController _effects;
private readonly ILiveRenderProjectionSink? _renderProjections;
public LiveObjectFrameController(
RetailInboundEventDispatcher inboundEvents,
@ -149,7 +151,8 @@ internal sealed class LiveObjectFrameController : ILiveObjectFramePhase
LiveEntityAnimationPresenter animationPresenter,
LiveEntityAnimationRuntimeView<LiveEntityAnimationState> animatedEntities,
EquippedChildRenderController equippedChildren,
LiveEffectFrameController effects)
LiveEffectFrameController effects,
ILiveRenderProjectionSink? renderProjections = null)
{
_inboundEvents = inboundEvents ?? throw new ArgumentNullException(nameof(inboundEvents));
_localPlayerFrame = localPlayerFrame
@ -168,6 +171,7 @@ internal sealed class LiveObjectFrameController : ILiveObjectFramePhase
_equippedChildren = equippedChildren
?? throw new ArgumentNullException(nameof(equippedChildren));
_effects = effects ?? throw new ArgumentNullException(nameof(effects));
_renderProjections = renderProjections;
}
public void Tick(float deltaSeconds) =>
@ -202,6 +206,7 @@ internal sealed class LiveObjectFrameController : ILiveObjectFramePhase
if (_animatedEntities.Count > 0)
_animationPresenter.Present(schedules);
_equippedChildren.Tick();
_renderProjections?.SynchronizeActiveSources();
// Retail CPhysicsObj::animate_static_object @ 0x00513DF0 runs
// Script -> Particle -> process_hooks. acdream intentionally retains
// the TS-51 static-order difference here: ProcessHooks precedes the
@ -221,24 +226,28 @@ internal sealed class LiveSpatialPresentationReconciler : ILiveSpatialReconcileP
private readonly EquippedChildRenderController _equippedChildren;
private readonly ParticleHookSink _particleSink;
private readonly LiveEntityLightController _lights;
private readonly ILiveRenderProjectionSink? _renderProjections;
public LiveSpatialPresentationReconciler(
EntityEffectController entityEffects,
EquippedChildRenderController equippedChildren,
ParticleHookSink particleSink,
LiveEntityLightController lights)
LiveEntityLightController lights,
ILiveRenderProjectionSink? renderProjections = null)
{
_entityEffects = entityEffects ?? throw new ArgumentNullException(nameof(entityEffects));
_equippedChildren = equippedChildren
?? throw new ArgumentNullException(nameof(equippedChildren));
_particleSink = particleSink ?? throw new ArgumentNullException(nameof(particleSink));
_lights = lights ?? throw new ArgumentNullException(nameof(lights));
_renderProjections = renderProjections;
}
public void Reconcile()
{
_entityEffects.RefreshLiveOwnerPoses();
_equippedChildren.Tick();
_renderProjections?.SynchronizeActiveSources();
_particleSink.RefreshAttachedEmitters();
_lights.Refresh();
}