fix(render): harden portal exit handoff
This commit is contained in:
parent
ddbd7e4096
commit
82e4b4cb6d
17 changed files with 896 additions and 119 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering.Gpu;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.Content.Vfx;
|
||||
using AcDream.Core.Lighting;
|
||||
|
|
@ -96,6 +97,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
private float _rotationEndAngle;
|
||||
private float _rotationCurrentAngle;
|
||||
private bool _waitCueVisible;
|
||||
private bool _probeFreezeReached;
|
||||
private bool _disposeRequested;
|
||||
private bool _disposing;
|
||||
private bool _disposed;
|
||||
|
|
@ -256,6 +258,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
_rotationCurrentAngle = 0f;
|
||||
_camera.DirectionDegrees = 0f;
|
||||
_waitCueVisible = false;
|
||||
_probeFreezeReached = false;
|
||||
_visible = true;
|
||||
RebuildPose();
|
||||
}
|
||||
|
|
@ -267,6 +270,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
return;
|
||||
_visible = false;
|
||||
_waitCueVisible = false;
|
||||
_probeFreezeReached = false;
|
||||
_animationHooks.Clear();
|
||||
_sequence.ClearAnimations();
|
||||
}
|
||||
|
|
@ -275,11 +279,25 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
{
|
||||
if (!_visible || dt < 0f)
|
||||
return;
|
||||
if (_probeFreezeReached)
|
||||
return;
|
||||
|
||||
_sequence.Update(dt, frame: null);
|
||||
RebuildPose();
|
||||
_animationHooks.Drain(Vector3.Zero);
|
||||
TickRotation(dt);
|
||||
|
||||
if (StreamingDiagnostics.TunnelFreezeFrame is not { } freezeFrame
|
||||
|| CurrentAnimationFrame < freezeFrame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_probeFreezeReached = true;
|
||||
Console.WriteLine(
|
||||
$"[tunnel-freeze] frame={CurrentAnimationFrame} "
|
||||
+ $"target={freezeFrame} setup=0x{_setupDid:X8} "
|
||||
+ $"animation=0x{_animationDid:X8} state=held");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -128,9 +128,10 @@ internal readonly record struct ProcessResourceDiagnostics(
|
|||
int TrackedGpuTextures);
|
||||
|
||||
/// <summary>
|
||||
/// Immutable resource facts captured only when explicit UI-probe dumping is enabled.
|
||||
/// Grouping keeps the diagnostics controller independent from every canonical renderer,
|
||||
/// VFX, mesh, texture, and process owner used to produce the values.
|
||||
/// Immutable resource facts captured only by an explicit low-frequency
|
||||
/// diagnostic such as UI-probe dumping or reveal timing. Grouping keeps the
|
||||
/// consumers independent from every canonical renderer, VFX, mesh, texture,
|
||||
/// and process owner used to produce the values.
|
||||
/// </summary>
|
||||
internal readonly record struct RenderFrameResourceDiagnosticsSnapshot(
|
||||
VfxStreamResourceDiagnostics Vfx,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public sealed class TeleportViewPlaneController
|
|||
public const float TransitionViewPlaneDistance = 0.001f;
|
||||
|
||||
private float _gameViewPlaneDistance = 1f;
|
||||
private TeleportAnimState _state = TeleportAnimState.Off;
|
||||
private readonly ProjectionOverrideCamera _projectionCamera = new();
|
||||
|
||||
public bool Enabled { get; private set; }
|
||||
|
|
@ -36,6 +37,7 @@ public sealed class TeleportViewPlaneController
|
|||
|
||||
_gameViewPlaneDistance = distance;
|
||||
CurrentViewPlaneDistance = distance;
|
||||
_state = TeleportAnimState.Off;
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +50,7 @@ public sealed class TeleportViewPlaneController
|
|||
/// </summary>
|
||||
public void Update(TeleportAnimSnapshot snapshot)
|
||||
{
|
||||
_state = snapshot.State;
|
||||
switch (snapshot.State)
|
||||
{
|
||||
case TeleportAnimState.WorldFadeOut:
|
||||
|
|
@ -80,6 +83,7 @@ public sealed class TeleportViewPlaneController
|
|||
|
||||
public void Reset()
|
||||
{
|
||||
_state = TeleportAnimState.Off;
|
||||
Enabled = false;
|
||||
CurrentViewPlaneDistance = _gameViewPlaneDistance;
|
||||
}
|
||||
|
|
@ -110,13 +114,35 @@ public sealed class TeleportViewPlaneController
|
|||
|
||||
float distance = MathF.Max(CurrentViewPlaneDistance, TransitionViewPlaneDistance);
|
||||
float fov = 2f * MathF.Atan(1f / distance);
|
||||
float near = MathF.Max(0.1f, distance * 0.25f);
|
||||
float near = WorldTransitionNearPlane(distance);
|
||||
if (near >= far)
|
||||
near = MathF.Min(0.1f, far * 0.5f);
|
||||
|
||||
return Matrix4x4.CreatePerspectiveFieldOfView(fov, aspect, near, far);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's <c>set_vdst</c> keeps <c>znear</c> at 0.1 m below a view-plane
|
||||
/// distance of 0.4. The legacy landscape path still supplied a complete
|
||||
/// projected screen at the singular teleport endpoint. Vulkan clips the
|
||||
/// finite resident terrain geometrically: at distance 0.001, every lower-
|
||||
/// viewport ground ray hits the terrain before 0.1 m and the authored sky
|
||||
/// is exposed underneath it. Scale the near plane with the transition only
|
||||
/// while the world viewport owns the frame. This preserves the exact retail
|
||||
/// X/Y warp, the tunnel projection, and the ordinary game projection while
|
||||
/// giving the modern terrain path the coverage retail visibly produced.
|
||||
/// </summary>
|
||||
private float WorldTransitionNearPlane(float distance)
|
||||
{
|
||||
if (_state is TeleportAnimState.WorldFadeOut or TeleportAnimState.WorldFadeIn
|
||||
&& distance < 0.4f)
|
||||
{
|
||||
return MathF.Max(0.0001f, distance * 0.25f);
|
||||
}
|
||||
|
||||
return MathF.Max(0.1f, distance * 0.25f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decorate the active camera with the same projection returned by
|
||||
/// <see cref="Apply(Matrix4x4)"/>. Retail's <c>Render::set_vdst</c> is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue