feat(render): implement Campaign AR and terrain fidelity
This commit is contained in:
parent
99cf26e00c
commit
7a5f96ede5
368 changed files with 50611 additions and 950 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Selection;
|
||||
using AcDream.App.Rendering.Packs;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.Core.Rendering;
|
||||
|
|
@ -15,6 +16,28 @@ internal interface IWorldScenePViewRenderer
|
|||
void AbortFrame();
|
||||
}
|
||||
|
||||
internal readonly record struct PreparedWorldSceneFrame(
|
||||
bool ShouldRender,
|
||||
RenderFrameFoundation Foundation,
|
||||
WorldRenderFrame World,
|
||||
int ActiveDayGroup);
|
||||
|
||||
/// <summary>
|
||||
/// Pack-on-only two-stage seam. Preparation resolves the canonical camera and
|
||||
/// borrowed world roots once; the shadow prepass may consume those exact facts
|
||||
/// before the normal world pass executes them without a second PView/build.
|
||||
/// </summary>
|
||||
internal interface IPreparedWorldSceneFramePhase : IWorldSceneFramePhase
|
||||
{
|
||||
PreparedWorldSceneFrame PrepareEnhanced(RenderFrameInput input);
|
||||
|
||||
WorldRenderFrameOutcome RenderPreparedEnhanced(
|
||||
RenderFrameInput input,
|
||||
in PreparedWorldSceneFrame prepared);
|
||||
|
||||
void CancelPreparedEnhanced(in PreparedWorldSceneFrame prepared);
|
||||
}
|
||||
|
||||
internal sealed class WorldScenePViewRenderer : IWorldScenePViewRenderer
|
||||
{
|
||||
private readonly RetailPViewRenderer _renderer;
|
||||
|
|
@ -46,7 +69,7 @@ internal sealed class WorldScenePViewRenderer : IWorldScenePViewRenderer
|
|||
/// decision: a resolved viewer root uses DrawInside; only an unresolved root
|
||||
/// uses the flat safety path.
|
||||
/// </summary>
|
||||
internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
||||
internal sealed class WorldSceneRenderer : IPreparedWorldSceneFramePhase
|
||||
{
|
||||
private readonly IRenderFrameFoundationSource _foundation;
|
||||
private readonly IRenderLoginStateSource _login;
|
||||
|
|
@ -62,7 +85,10 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
private readonly IWorldRenderRangeSource _renderRange;
|
||||
private readonly IWorldSceneDiagnostics _diagnostics;
|
||||
private readonly IWorldGenerationAvailability _availability;
|
||||
private readonly IAtmosphericWorldFrameSink? _atmosphere;
|
||||
private readonly RetailPViewFrameInput _pviewFrameInput = new();
|
||||
private WorldRenderFrame _preparedEnhancedWorld;
|
||||
private bool _hasPreparedEnhancedWorld;
|
||||
|
||||
public WorldSceneRenderer(
|
||||
IRenderFrameFoundationSource foundation,
|
||||
|
|
@ -78,7 +104,8 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
IWorldScenePassExecutor passes,
|
||||
IWorldRenderRangeSource renderRange,
|
||||
IWorldSceneDiagnostics diagnostics,
|
||||
IWorldGenerationAvailability? availability = null)
|
||||
IWorldGenerationAvailability? availability = null,
|
||||
IAtmosphericWorldFrameSink? atmosphere = null)
|
||||
{
|
||||
_foundation = foundation ?? throw new ArgumentNullException(nameof(foundation));
|
||||
_login = login ?? throw new ArgumentNullException(nameof(login));
|
||||
|
|
@ -95,6 +122,7 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
_renderRange = renderRange ?? throw new ArgumentNullException(nameof(renderRange));
|
||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||
_availability = availability ?? AlwaysAvailableWorldGeneration.Instance;
|
||||
_atmosphere = atmosphere;
|
||||
}
|
||||
|
||||
public WorldRenderFrameOutcome Render(RenderFrameInput input)
|
||||
|
|
@ -105,7 +133,15 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
bool pviewFrameStarted = false;
|
||||
try
|
||||
{
|
||||
_selection?.BeginFrame();
|
||||
// Enhanced rendering builds the canonical camera before this world
|
||||
// transaction so the shadow prepass can consume it. Carry that
|
||||
// exact frustum across BeginFrame: clearing it here would make every
|
||||
// subsequently drawn part fail RetailSelectionScene's visibility
|
||||
// gate, leaving radar selection alive but disabling world picking.
|
||||
FrustumPlanes? preparedSelectionFrustum = _hasPreparedEnhancedWorld
|
||||
? _preparedEnhancedWorld.Camera.Frustum
|
||||
: null;
|
||||
_selection?.BeginFrame(preparedSelectionFrustum);
|
||||
if (!_availability.IsWorldAvailable)
|
||||
{
|
||||
_selection?.CompleteFrame();
|
||||
|
|
@ -125,10 +161,23 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
// the same abort path instead of making every later frame fail.
|
||||
worldFrameStarted = true;
|
||||
_alpha.BeginFrame();
|
||||
WorldRenderFrame world = _frames.Build(
|
||||
WorldRenderFrame world;
|
||||
if (_hasPreparedEnhancedWorld)
|
||||
{
|
||||
world = _preparedEnhancedWorld;
|
||||
_hasPreparedEnhancedWorld = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
world = _frames.Build(
|
||||
in foundation,
|
||||
_login.IsWaitingForLogin,
|
||||
_sky.ActiveDayGroup);
|
||||
}
|
||||
_atmosphere?.Publish(
|
||||
in foundation,
|
||||
_login.IsWaitingForLogin,
|
||||
_sky.ActiveDayGroup);
|
||||
in world,
|
||||
_sky.ActiveDayGroupIndex);
|
||||
_passes.BeginFrame();
|
||||
|
||||
WorldCameraFrame camera = world.Camera;
|
||||
|
|
@ -311,6 +360,78 @@ internal sealed class WorldSceneRenderer : IWorldSceneFramePhase
|
|||
}
|
||||
}
|
||||
|
||||
public PreparedWorldSceneFrame PrepareEnhanced(RenderFrameInput input)
|
||||
{
|
||||
_ = input;
|
||||
if (_hasPreparedEnhancedWorld)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The prior enhanced world preparation was not consumed.");
|
||||
}
|
||||
|
||||
RenderFrameFoundation foundation = _foundation.Foundation;
|
||||
if (!_availability.IsWorldAvailable || foundation.PortalViewportVisible)
|
||||
return new PreparedWorldSceneFrame(false, foundation, default, -1);
|
||||
|
||||
WorldRenderFrame world = _frames.Build(
|
||||
in foundation,
|
||||
_login.IsWaitingForLogin,
|
||||
_sky.ActiveDayGroup);
|
||||
WorldRootFrame roots = world.Roots;
|
||||
world = world with
|
||||
{
|
||||
ResidentStreamingWindow =
|
||||
_entities.CaptureResidentStreamingWindow(
|
||||
roots.RenderCenterLandblockX,
|
||||
roots.RenderCenterLandblockY),
|
||||
CelestialShadowSource =
|
||||
AuthoredCelestialShadowSourceResolver.Resolve(
|
||||
_sky.ActiveDayGroup,
|
||||
_sky.DayFraction,
|
||||
foundation.Sky),
|
||||
};
|
||||
_preparedEnhancedWorld = world;
|
||||
_hasPreparedEnhancedWorld = true;
|
||||
return new PreparedWorldSceneFrame(
|
||||
true,
|
||||
foundation,
|
||||
world,
|
||||
_sky.ActiveDayGroupIndex);
|
||||
}
|
||||
|
||||
public WorldRenderFrameOutcome RenderPreparedEnhanced(
|
||||
RenderFrameInput input,
|
||||
in PreparedWorldSceneFrame prepared)
|
||||
{
|
||||
if (prepared.ShouldRender != _hasPreparedEnhancedWorld)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The prepared enhanced-world token does not match the pending frame.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Render(input);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// A gate may change between prepare and execute. Never let that
|
||||
// exceptional edge leak borrowed builder scratch into another frame.
|
||||
_hasPreparedEnhancedWorld = false;
|
||||
_preparedEnhancedWorld = default;
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelPreparedEnhanced(in PreparedWorldSceneFrame prepared)
|
||||
{
|
||||
if (!prepared.ShouldRender)
|
||||
return;
|
||||
if (!_hasPreparedEnhancedWorld)
|
||||
return;
|
||||
_hasPreparedEnhancedWorld = false;
|
||||
_preparedEnhancedWorld = default;
|
||||
}
|
||||
|
||||
private void CompleteWorldFrame()
|
||||
{
|
||||
_alpha.EndFrame();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue