refactor(runtime): extract the live object frame
This commit is contained in:
parent
99a3e819c4
commit
4e4aac2c5a
27 changed files with 1217 additions and 371 deletions
246
src/AcDream.App/Update/LiveObjectFrameController.cs
Normal file
246
src/AcDream.App/Update/LiveObjectFrameController.cs
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Input;
|
||||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.Vfx;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.UI.Abstractions.Panels.Settings;
|
||||
|
||||
namespace AcDream.App.Update;
|
||||
|
||||
internal interface ILiveObjectFramePhase
|
||||
{
|
||||
void Tick(float deltaSeconds);
|
||||
}
|
||||
|
||||
internal interface ILiveSessionFramePhase
|
||||
{
|
||||
void Tick();
|
||||
}
|
||||
|
||||
internal interface IPostNetworkCommandFramePhase
|
||||
{
|
||||
void RunPostNetworkCommandPhase();
|
||||
}
|
||||
|
||||
internal interface ILiveSpatialReconcilePhase
|
||||
{
|
||||
void Reconcile();
|
||||
}
|
||||
|
||||
internal interface IParticleRangeSource
|
||||
{
|
||||
float RangeMultiplier { get; }
|
||||
}
|
||||
|
||||
internal sealed class SettingsParticleRangeSource : IParticleRangeSource
|
||||
{
|
||||
private readonly SettingsVM? _settings;
|
||||
private readonly ParticleRange _fallback;
|
||||
|
||||
public SettingsParticleRangeSource(SettingsVM? settings, ParticleRange fallback)
|
||||
{
|
||||
_settings = settings;
|
||||
_fallback = fallback;
|
||||
}
|
||||
|
||||
public float RangeMultiplier =>
|
||||
(_settings?.DisplayDraft.ParticleRange ?? _fallback) == ParticleRange.Extended
|
||||
? ParticleVisibilityController.ExtendedRangeMultiplier
|
||||
: 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Owns the shared effect-manager tail that follows ordinary and static
|
||||
/// object worksets in acdream's registered TS-50/TS-51 adaptation.
|
||||
/// </summary>
|
||||
internal sealed class LiveEffectFrameController
|
||||
{
|
||||
private readonly TranslucencyFadeManager _translucencyFades;
|
||||
private readonly AnimationHookFrameQueue _animationHooks;
|
||||
private readonly EntityEffectController _entityEffects;
|
||||
private readonly ParticleHookSink _particleSink;
|
||||
private readonly LiveEntityLightController _lights;
|
||||
private readonly ParticleVisibilityController _particleVisibility;
|
||||
private readonly ParticleSystem _particles;
|
||||
private readonly PhysicsScriptRunner _scripts;
|
||||
private readonly IPhysicsScriptTimeSource _scriptTime;
|
||||
private readonly IParticleRangeSource _particleRange;
|
||||
|
||||
public LiveEffectFrameController(
|
||||
TranslucencyFadeManager translucencyFades,
|
||||
AnimationHookFrameQueue animationHooks,
|
||||
EntityEffectController entityEffects,
|
||||
ParticleHookSink particleSink,
|
||||
LiveEntityLightController lights,
|
||||
ParticleVisibilityController particleVisibility,
|
||||
ParticleSystem particles,
|
||||
PhysicsScriptRunner scripts,
|
||||
IPhysicsScriptTimeSource scriptTime,
|
||||
IParticleRangeSource particleRange)
|
||||
{
|
||||
_translucencyFades = translucencyFades
|
||||
?? throw new ArgumentNullException(nameof(translucencyFades));
|
||||
_animationHooks = animationHooks ?? throw new ArgumentNullException(nameof(animationHooks));
|
||||
_entityEffects = entityEffects ?? throw new ArgumentNullException(nameof(entityEffects));
|
||||
_particleSink = particleSink ?? throw new ArgumentNullException(nameof(particleSink));
|
||||
_lights = lights ?? throw new ArgumentNullException(nameof(lights));
|
||||
_particleVisibility = particleVisibility
|
||||
?? throw new ArgumentNullException(nameof(particleVisibility));
|
||||
_particles = particles ?? throw new ArgumentNullException(nameof(particles));
|
||||
_scripts = scripts ?? throw new ArgumentNullException(nameof(scripts));
|
||||
_scriptTime = scriptTime ?? throw new ArgumentNullException(nameof(scriptTime));
|
||||
_particleRange = particleRange ?? throw new ArgumentNullException(nameof(particleRange));
|
||||
}
|
||||
|
||||
public void Tick(float deltaSeconds)
|
||||
{
|
||||
// Retail's ordinary-object UpdatePositionInternal @ 0x00512C30 calls
|
||||
// CPhysicsObj::process_hooks @ 0x00511550 before its manager tail.
|
||||
// acdream currently keeps non-AnimationDone hooks at this deferred
|
||||
// shared boundary under TS-50; this extraction must not silently
|
||||
// claim that adaptation is gone.
|
||||
_translucencyFades.AdvanceAll(deltaSeconds);
|
||||
_animationHooks.Drain();
|
||||
_entityEffects.RefreshLiveOwnerPoses();
|
||||
_particleSink.RefreshAttachedEmitters();
|
||||
_lights.Refresh();
|
||||
_particleVisibility.Apply(_particles, _particleRange.RangeMultiplier);
|
||||
|
||||
// Retail CPhysicsObj::UpdateObjectInternal @ 0x005156B0 advances the
|
||||
// ordinary-object Particle manager before Script. acdream's shared
|
||||
// once-per-host tail and its static-order difference remain TS-51.
|
||||
_particles.Tick(deltaSeconds);
|
||||
_scripts.Tick(_scriptTime.CurrentScriptTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Owns the complete pre-network local/ordinary/static live-object phase.
|
||||
/// </summary>
|
||||
internal sealed class LiveObjectFrameController : ILiveObjectFramePhase
|
||||
{
|
||||
private readonly RetailInboundEventDispatcher _inboundEvents;
|
||||
private readonly RetailLocalPlayerFrameController _localPlayerFrame;
|
||||
private readonly SelectionInteractionController? _selectionInteractions;
|
||||
private readonly LiveEntityRuntime _liveEntities;
|
||||
private readonly ILocalPlayerIdentitySource _localPlayer;
|
||||
private readonly LiveWorldOriginState _origin;
|
||||
private readonly LiveEntityAnimationScheduler _animations;
|
||||
private readonly RetailStaticAnimatingObjectScheduler _staticAnimations;
|
||||
private readonly LiveEntityAnimationPresenter _animationPresenter;
|
||||
private readonly LiveEntityAnimationRuntimeView<LiveEntityAnimationState>
|
||||
_animatedEntities;
|
||||
private readonly EquippedChildRenderController _equippedChildren;
|
||||
private readonly LiveEffectFrameController _effects;
|
||||
|
||||
public LiveObjectFrameController(
|
||||
RetailInboundEventDispatcher inboundEvents,
|
||||
RetailLocalPlayerFrameController localPlayerFrame,
|
||||
SelectionInteractionController? selectionInteractions,
|
||||
LiveEntityRuntime liveEntities,
|
||||
ILocalPlayerIdentitySource localPlayer,
|
||||
LiveWorldOriginState origin,
|
||||
LiveEntityAnimationScheduler animations,
|
||||
RetailStaticAnimatingObjectScheduler staticAnimations,
|
||||
LiveEntityAnimationPresenter animationPresenter,
|
||||
LiveEntityAnimationRuntimeView<LiveEntityAnimationState> animatedEntities,
|
||||
EquippedChildRenderController equippedChildren,
|
||||
LiveEffectFrameController effects)
|
||||
{
|
||||
_inboundEvents = inboundEvents ?? throw new ArgumentNullException(nameof(inboundEvents));
|
||||
_localPlayerFrame = localPlayerFrame
|
||||
?? throw new ArgumentNullException(nameof(localPlayerFrame));
|
||||
_selectionInteractions = selectionInteractions;
|
||||
_liveEntities = liveEntities ?? throw new ArgumentNullException(nameof(liveEntities));
|
||||
_localPlayer = localPlayer ?? throw new ArgumentNullException(nameof(localPlayer));
|
||||
_origin = origin ?? throw new ArgumentNullException(nameof(origin));
|
||||
_animations = animations ?? throw new ArgumentNullException(nameof(animations));
|
||||
_staticAnimations = staticAnimations
|
||||
?? throw new ArgumentNullException(nameof(staticAnimations));
|
||||
_animationPresenter = animationPresenter
|
||||
?? throw new ArgumentNullException(nameof(animationPresenter));
|
||||
_animatedEntities = animatedEntities
|
||||
?? throw new ArgumentNullException(nameof(animatedEntities));
|
||||
_equippedChildren = equippedChildren
|
||||
?? throw new ArgumentNullException(nameof(equippedChildren));
|
||||
_effects = effects ?? throw new ArgumentNullException(nameof(effects));
|
||||
}
|
||||
|
||||
public void Tick(float deltaSeconds) =>
|
||||
_inboundEvents.Run(
|
||||
this,
|
||||
deltaSeconds,
|
||||
static (controller, elapsed) => controller.TickCore(elapsed));
|
||||
|
||||
private void TickCore(float deltaSeconds)
|
||||
{
|
||||
_localPlayerFrame.AdvanceBeforeNetwork(deltaSeconds);
|
||||
_selectionInteractions?.DrainOutbound();
|
||||
|
||||
Vector3? playerPosition =
|
||||
_liveEntities.MaterializedWorldEntities.TryGetValue(
|
||||
_localPlayer.ServerGuid,
|
||||
out WorldEntity? player)
|
||||
? player.Position
|
||||
: null;
|
||||
IReadOnlyDictionary<uint, LiveEntityAnimationSchedule> schedules =
|
||||
_animations.Tick(
|
||||
deltaSeconds,
|
||||
playerPosition,
|
||||
_localPlayerFrame.HiddenPartPoseDirty,
|
||||
_origin.CenterX,
|
||||
_origin.CenterY,
|
||||
_animationPresenter.PrepareAnimation);
|
||||
|
||||
// CPhysics::UseTime @ 0x00509950 walks ordinary objects before the
|
||||
// distinct static_animating_objects workset.
|
||||
_staticAnimations.Tick(deltaSeconds);
|
||||
if (_animatedEntities.Count > 0)
|
||||
_animationPresenter.Present(schedules);
|
||||
_equippedChildren.Tick();
|
||||
// Retail CPhysicsObj::animate_static_object @ 0x00513DF0 runs
|
||||
// Script -> Particle -> process_hooks. acdream intentionally retains
|
||||
// the TS-51 static-order difference here: ProcessHooks precedes the
|
||||
// shared once-per-host Particle -> Script tail below.
|
||||
_staticAnimations.ProcessHooks();
|
||||
_effects.Tick(deltaSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-composes spatial derivatives after authoritative root mutations without
|
||||
/// advancing animation, physics, effects, particles, scripts, or fades.
|
||||
/// </summary>
|
||||
internal sealed class LiveSpatialPresentationReconciler : ILiveSpatialReconcilePhase
|
||||
{
|
||||
private readonly EntityEffectController _entityEffects;
|
||||
private readonly EquippedChildRenderController _equippedChildren;
|
||||
private readonly ParticleHookSink _particleSink;
|
||||
private readonly LiveEntityLightController _lights;
|
||||
|
||||
public LiveSpatialPresentationReconciler(
|
||||
EntityEffectController entityEffects,
|
||||
EquippedChildRenderController equippedChildren,
|
||||
ParticleHookSink particleSink,
|
||||
LiveEntityLightController lights)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
|
||||
public void Reconcile()
|
||||
{
|
||||
_entityEffects.RefreshLiveOwnerPoses();
|
||||
_equippedChildren.Tick();
|
||||
_particleSink.RefreshAttachedEmitters();
|
||||
_lights.Refresh();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue