fix(render): restore landscape objects and walk alpha order
This commit is contained in:
parent
4683ac6f72
commit
e880860291
5 changed files with 275 additions and 21 deletions
|
|
@ -177,7 +177,10 @@ public sealed class RetailFrameWalk
|
|||
// outdoor-static turn.
|
||||
if (block.CellBuildings[cellIndex] is WalkBuilding building)
|
||||
DrawBuilding(building, activeViews, ctx, sink);
|
||||
sink.OnLandscapeCellTurn((block.LandblockId & 0xFFFF0000u) | (uint)(cellIndex + 1));
|
||||
sink.OnLandscapeCellTurn(
|
||||
block.LandblockId,
|
||||
block.SideCellCount,
|
||||
cellIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,19 @@ public interface IWalkEventSink
|
|||
/// </summary>
|
||||
void OnLandscapeCellTurn(uint cellId) { }
|
||||
|
||||
/// <summary>
|
||||
/// The LOD-aware sibling of <see cref="OnLandscapeCellTurn(uint)"/>.
|
||||
/// <paramref name="cellIndex"/> names one cell in the block's CURRENT
|
||||
/// <paramref name="sideCellCount"/>×<paramref name="sideCellCount"/>
|
||||
/// coarse terrain grid. Retail attaches every outdoor object's shadow to
|
||||
/// that coarse <c>CObjCell</c>; acdream retains the authoritative 8×8
|
||||
/// owner buckets, so production consumers expand this turn back to the
|
||||
/// covered original cells. The default preserves the FW1 vocabulary and
|
||||
/// every diagnostic sink that only needs the coarse turn.</summary>
|
||||
void OnLandscapeCellTurn(uint landblockId, int sideCellCount, int cellIndex) =>
|
||||
OnLandscapeCellTurn(
|
||||
(landblockId & 0xFFFF0000u) | checked((uint)(cellIndex + 1)));
|
||||
|
||||
/// <summary>
|
||||
/// Fires at <see cref="RetailFrameWalk.DrawBuilding"/> once retail's own
|
||||
/// gate has passed — <c>RenderDeviceD3D::DrawBuilding</c> @0x0059f2a0
|
||||
|
|
|
|||
|
|
@ -223,6 +223,13 @@ internal enum WalkFrameEventKind : byte
|
|||
/// was recorded.</summary>
|
||||
StreamMark,
|
||||
|
||||
/// <summary>Submits every retained translucent walk batch through
|
||||
/// <see cref="WbDrawDispatcher.SubmitWalkAlphaInstance"/> up to the
|
||||
/// exclusive-end index in <see cref="WalkFrameEvent.IntArg"/>. This is a
|
||||
/// replay-time event: collecting the complete frame must not make alpha
|
||||
/// from a later/near cell visible to an earlier building barrier.</summary>
|
||||
AlphaSubmitMark,
|
||||
|
||||
/// <summary><see cref="IWalkFrameLeafRenderer.DrawSky"/>.</summary>
|
||||
Sky,
|
||||
|
||||
|
|
@ -351,6 +358,9 @@ internal readonly struct WalkFrameEvent
|
|||
internal static WalkFrameEvent Mark(int exclusiveEnd) =>
|
||||
new(WalkFrameEventKind.StreamMark, exclusiveEnd, 0, 0f, null);
|
||||
|
||||
internal static WalkFrameEvent AlphaSubmitMark(int exclusiveEnd) =>
|
||||
new(WalkFrameEventKind.AlphaSubmitMark, exclusiveEnd, 0, 0f, null);
|
||||
|
||||
internal static WalkFrameEvent Sky() =>
|
||||
new(WalkFrameEventKind.Sky, 0, 0, 0f, null);
|
||||
|
||||
|
|
@ -472,6 +482,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
private readonly OrderedDrawStream _stream = new();
|
||||
private readonly List<WalkFrameEvent> _events = new();
|
||||
private readonly List<int> _markPositions = new();
|
||||
private readonly List<WbDrawDispatcher.WalkClassifiedBatch> _alphaSubmissions = new();
|
||||
private int _alphaSubmitMark;
|
||||
|
||||
// Campaign FW3.4a: visited-set collection, absorbed from the renderer's
|
||||
// former dedicated set-collecting sink (RetailPViewRenderer's old
|
||||
|
|
@ -603,6 +615,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_stream.Reset();
|
||||
_events.Clear();
|
||||
_markPositions.Clear();
|
||||
_alphaSubmissions.Clear();
|
||||
_alphaSubmitMark = 0;
|
||||
VisitedCells.Clear();
|
||||
LookInCellTurns.Clear();
|
||||
_lookInTurns.Clear();
|
||||
|
|
@ -736,6 +750,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_stream.Reset();
|
||||
_events.Clear();
|
||||
_markPositions.Clear();
|
||||
_alphaSubmissions.Clear();
|
||||
_alphaSubmitMark = 0;
|
||||
VisitedCells.Clear();
|
||||
LookInCellTurns.Clear();
|
||||
_lookInTurns.Clear();
|
||||
|
|
@ -764,6 +780,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
try
|
||||
{
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -804,6 +821,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_dispatcher.PrepareOrderedStream(frame, _stream, _viewProjection, _markPositions);
|
||||
|
||||
int cursor = 0;
|
||||
int alphaCursor = 0;
|
||||
for (int i = 0; i < _events.Count; i++)
|
||||
{
|
||||
WalkFrameEvent e = _events[i];
|
||||
|
|
@ -817,6 +835,18 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_dispatcher.DrawOrderedRange(encoder, cursor, count);
|
||||
cursor = end;
|
||||
break;
|
||||
case WalkFrameEventKind.AlphaSubmitMark:
|
||||
int alphaEnd = e.IntArg;
|
||||
for (; alphaCursor < alphaEnd; alphaCursor++)
|
||||
{
|
||||
WbDrawDispatcher.WalkClassifiedBatch batch =
|
||||
_alphaSubmissions[alphaCursor];
|
||||
_dispatcher.SubmitWalkAlphaInstance(
|
||||
in batch,
|
||||
_cameraWorldPosition,
|
||||
_viewProjection);
|
||||
}
|
||||
break;
|
||||
case WalkFrameEventKind.Sky:
|
||||
_leafRenderer.DrawSky();
|
||||
break;
|
||||
|
|
@ -883,6 +913,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_stream.Reset();
|
||||
_events.Clear();
|
||||
_markPositions.Clear();
|
||||
_alphaSubmissions.Clear();
|
||||
_alphaSubmitMark = 0;
|
||||
_readyToReplay = false;
|
||||
_ctx = null;
|
||||
}
|
||||
|
|
@ -916,6 +948,50 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
}
|
||||
|
||||
void IWalkEventSink.OnLandscapeCellTurn(uint cellId)
|
||||
=> HandleLandscapeCellTurn(cellId);
|
||||
|
||||
void IWalkEventSink.OnLandscapeCellTurn(
|
||||
uint landblockId,
|
||||
int sideCellCount,
|
||||
int cellIndex)
|
||||
{
|
||||
if (sideCellCount is not (1 or 2 or 4 or 8))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(sideCellCount),
|
||||
sideCellCount,
|
||||
"A landscape LOD grid must be 1, 2, 4, or 8 cells per side.");
|
||||
}
|
||||
if ((uint)cellIndex >= (uint)(sideCellCount * sideCellCount))
|
||||
throw new ArgumentOutOfRangeException(nameof(cellIndex));
|
||||
|
||||
uint blockPrefix = landblockId & 0xFFFF0000u;
|
||||
if (sideCellCount == 8)
|
||||
{
|
||||
HandleLandscapeCellTurn(blockPrefix | checked((uint)(cellIndex + 1)));
|
||||
return;
|
||||
}
|
||||
|
||||
// CLandBlock's coarse DrawSortCell owns the shadows from every
|
||||
// original 24 m land cell it covers. Our render journal deliberately
|
||||
// retains those authoritative 8x8 owner buckets, so turn all covered
|
||||
// buckets here while preserving the coarse cell's single walk turn.
|
||||
int span = 8 / sideCellCount;
|
||||
int coarseX = cellIndex / sideCellCount;
|
||||
int coarseY = cellIndex % sideCellCount;
|
||||
int firstX = coarseX * span;
|
||||
int firstY = coarseY * span;
|
||||
for (int x = firstX; x < firstX + span; x++)
|
||||
{
|
||||
for (int y = firstY; y < firstY + span; y++)
|
||||
{
|
||||
HandleLandscapeCellTurn(
|
||||
blockPrefix | checked((uint)(x * 8 + y + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLandscapeCellTurn(uint cellId)
|
||||
{
|
||||
RequireOpenFrame();
|
||||
if (_landscapeViewRouteIndex < 0)
|
||||
|
|
@ -930,11 +1006,18 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_populator.PopulateOutdoorStatics(
|
||||
_stream, cellId, records.Records, records.TupleLandblockId,
|
||||
_cameraWorldPosition, _viewProjection,
|
||||
this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame);
|
||||
this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame,
|
||||
_alphaSubmissions);
|
||||
_populator.PopulateCellDynamics(
|
||||
_stream, cellId, dynamics.Records, dynamics.TupleLandblockId,
|
||||
_cameraWorldPosition, _viewProjection,
|
||||
this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame);
|
||||
this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame,
|
||||
_alphaSubmissions);
|
||||
if (_alphaSubmissions.Count != _alphaSubmitMark)
|
||||
{
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
}
|
||||
// FW4 (the #132 positional invariant): this cell's emitter owners
|
||||
// submit AT THIS TURN, so nearer buildings' pre-punch barriers
|
||||
// drain them against still-true depth — see
|
||||
|
|
@ -977,6 +1060,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
// look-ins) follows this call; the building's own shell content is
|
||||
// appended only once that pass completes (OnBuildingShellTurn).
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
_events.Add(WalkFrameEvent.AlphaBarrier());
|
||||
|
||||
_currentDcStage = WalkDrawStage.LookInStatic;
|
||||
|
|
@ -996,7 +1080,13 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
WalkFrameStaticRecords shell = _worldData.GetBuildingShellStatics(building);
|
||||
_populator.PopulateCell(
|
||||
_stream, WalkDrawStage.BuildingShell, building.PositionCellId,
|
||||
shell.Records, shell.TupleLandblockId, _cameraWorldPosition, _viewProjection);
|
||||
shell.Records, shell.TupleLandblockId, _cameraWorldPosition, _viewProjection,
|
||||
alphaSubmissions: _alphaSubmissions);
|
||||
if (_alphaSubmissions.Count != _alphaSubmitMark)
|
||||
{
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
}
|
||||
// FW4 (the #132 positional invariant): the building's own shell
|
||||
// emitters submit at the shell turn, after the shell content
|
||||
// flushes — see WalkFrameEventKind.StaticParticles.
|
||||
|
|
@ -1166,8 +1256,9 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_populator.PopulateCell(
|
||||
_stream, stage, cellId, records.Records, records.TupleLandblockId,
|
||||
_cameraWorldPosition, _viewProjection,
|
||||
this, viewRouteIndex);
|
||||
this, viewRouteIndex, _alphaSubmissions);
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
|
||||
if (stage == WalkDrawStage.LookInStatic)
|
||||
{
|
||||
|
|
@ -1184,8 +1275,10 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_cameraWorldPosition,
|
||||
_viewProjection,
|
||||
this,
|
||||
viewRouteIndex);
|
||||
viewRouteIndex,
|
||||
alphaSubmissions: _alphaSubmissions);
|
||||
MarkIfGrown();
|
||||
MarkAlphaIfGrown();
|
||||
if (HasAnyOwner(records) || HasAnyOwner(dynamics))
|
||||
_events.Add(WalkFrameEvent.CellParticles(cellId));
|
||||
}
|
||||
|
|
@ -1364,6 +1457,20 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
|
|||
_events.Add(WalkFrameEvent.Mark(count));
|
||||
}
|
||||
|
||||
/// <summary>Records translucent walk batches at the same temporal point
|
||||
/// as their cell turn. Replay submits them only when it reaches this
|
||||
/// marker, so an earlier building's full alpha barrier cannot see content
|
||||
/// that the far-to-near walk has not reached yet.</summary>
|
||||
private void MarkAlphaIfGrown()
|
||||
{
|
||||
int count = _alphaSubmissions.Count;
|
||||
if (count == _alphaSubmitMark)
|
||||
return;
|
||||
|
||||
_alphaSubmitMark = count;
|
||||
_events.Add(WalkFrameEvent.AlphaSubmitMark(count));
|
||||
}
|
||||
|
||||
private IWalkBuildingFrameContext RequireOpenFrame() =>
|
||||
_ctx ?? throw new InvalidOperationException(
|
||||
"WalkFrameDriver received a walk turn outside BeginFrame/EndFrame — call "
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ internal sealed class WalkStaticStreamPopulator
|
|||
Vector3 cameraWorldPosition,
|
||||
Matrix4x4 viewProjection,
|
||||
IWalkLookInViewSource? views = null,
|
||||
int viewRouteIndex = -1)
|
||||
int viewRouteIndex = -1,
|
||||
List<WbDrawDispatcher.WalkClassifiedBatch>? alphaSubmissions = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
for (int i = 0; i < records.Length; i++)
|
||||
|
|
@ -74,7 +75,7 @@ internal sealed class WalkStaticStreamPopulator
|
|||
ClassifyAndAppend(
|
||||
stream, stage, cellId, in records[i], tupleLandblockId,
|
||||
cameraWorldPosition, viewProjection,
|
||||
liveDynamic: false, views, viewRouteIndex);
|
||||
liveDynamic: false, views, viewRouteIndex, alphaSubmissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +99,8 @@ internal sealed class WalkStaticStreamPopulator
|
|||
Matrix4x4 viewProjection,
|
||||
IWalkLookInViewSource? views = null,
|
||||
int viewRouteIndex = -1,
|
||||
ISet<RenderProjectionId>? drawnOnce = null)
|
||||
ISet<RenderProjectionId>? drawnOnce = null,
|
||||
List<WbDrawDispatcher.WalkClassifiedBatch>? alphaSubmissions = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
for (int i = 0; i < records.Length; i++)
|
||||
|
|
@ -108,7 +110,7 @@ internal sealed class WalkStaticStreamPopulator
|
|||
ClassifyAndAppend(
|
||||
stream, WalkDrawStage.OutdoorStatic, cellId, in records[i],
|
||||
tupleLandblockId, cameraWorldPosition, viewProjection,
|
||||
liveDynamic: false, views, viewRouteIndex);
|
||||
liveDynamic: false, views, viewRouteIndex, alphaSubmissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +123,8 @@ internal sealed class WalkStaticStreamPopulator
|
|||
Matrix4x4 viewProjection,
|
||||
IWalkLookInViewSource? lookInViews = null,
|
||||
int lookInRouteIndex = -1,
|
||||
ISet<RenderProjectionId>? drawnOnce = null)
|
||||
ISet<RenderProjectionId>? drawnOnce = null,
|
||||
List<WbDrawDispatcher.WalkClassifiedBatch>? alphaSubmissions = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
for (int i = 0; i < records.Length; i++)
|
||||
|
|
@ -138,7 +141,8 @@ internal sealed class WalkStaticStreamPopulator
|
|||
viewProjection,
|
||||
liveDynamic: true,
|
||||
lookInViews,
|
||||
lookInRouteIndex);
|
||||
lookInRouteIndex,
|
||||
alphaSubmissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +156,8 @@ internal sealed class WalkStaticStreamPopulator
|
|||
Matrix4x4 viewProjection,
|
||||
bool liveDynamic = false,
|
||||
IWalkLookInViewSource? lookInViews = null,
|
||||
int lookInRouteIndex = -1)
|
||||
int lookInRouteIndex = -1,
|
||||
List<WbDrawDispatcher.WalkClassifiedBatch>? alphaSubmissions = null)
|
||||
{
|
||||
_batchScratch.Clear();
|
||||
_selectionScratch.Clear();
|
||||
|
|
@ -178,8 +183,15 @@ internal sealed class WalkStaticStreamPopulator
|
|||
}
|
||||
else
|
||||
{
|
||||
_dispatcher.SubmitWalkAlphaInstance(
|
||||
in batch, cameraWorldPosition, viewProjection);
|
||||
if (alphaSubmissions is null)
|
||||
{
|
||||
_dispatcher.SubmitWalkAlphaInstance(
|
||||
in batch, cameraWorldPosition, viewProjection);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaSubmissions.Add(batch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue