fix #451: stabilize portal seam rendering
This commit is contained in:
parent
f6fe0f2a4f
commit
1d2f2f738f
29 changed files with 1650 additions and 239 deletions
|
|
@ -134,6 +134,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
private readonly TerrainDrawDiagnosticsController _terrainDiagnostics;
|
||||
private readonly RetailPViewParticleClassifications _particleClassifications = new();
|
||||
private readonly HashSet<uint> _noSceneParticleEntityIds = [];
|
||||
private readonly Dictionary<uint, int> _singleCellClipRouting = new(1);
|
||||
private readonly Dictionary<uint, int> _noCellClipRouting = new(0);
|
||||
|
||||
/// <summary>
|
||||
/// Borrowed until the next late landscape pass. The outdoor-root post-world
|
||||
|
|
@ -207,6 +209,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
{
|
||||
List<Exception>? failures = null;
|
||||
TryAbort(_frameGlState.RestoreFrameDefaults);
|
||||
TryAbort(() => _envCells.SetClipRouting(null));
|
||||
TryAbort(_entities.ClearClipRouting);
|
||||
TryAbort(_particleClassifications.BeginFrame);
|
||||
TryAbort(_noSceneParticleEntityIds.Clear);
|
||||
if (failures is { Count: > 0 })
|
||||
|
|
@ -230,6 +234,11 @@ internal sealed class RetailPViewPassExecutor :
|
|||
ClipFrameAssembly reuseAssembly) =>
|
||||
ClipFrameAssembler.Assemble(_clipFrame, portalFrame, reuseAssembly);
|
||||
|
||||
public void AppendLookInClipFrames(
|
||||
IReadOnlyList<PortalVisibilityFrame> lookInFrames,
|
||||
ClipFrameAssembly assembly) =>
|
||||
ClipFrameAssembler.AppendLookInFrames(_clipFrame, lookInFrames, assembly);
|
||||
|
||||
public void PrepareClipFrame(int terrainUploadCount) =>
|
||||
_surface.PrepareClipFrame(terrainUploadCount);
|
||||
|
||||
|
|
@ -246,6 +255,24 @@ internal sealed class RetailPViewPassExecutor :
|
|||
_entities.ClearClipRouting();
|
||||
}
|
||||
|
||||
public void UseCellPortalViewRouting(uint cellId, ClipViewSlice slice)
|
||||
{
|
||||
_singleCellClipRouting.Clear();
|
||||
_singleCellClipRouting.Add(cellId, slice.Slot);
|
||||
_envCells.SetClipRouting(_singleCellClipRouting);
|
||||
// Retail DrawMesh only viewcone-checks an object's sphere under the
|
||||
// installed PortalList and then draws the mesh whole. Hard clipping the
|
||||
// object here slices a stationary player when the chase camera crosses
|
||||
// into the opposite cathedral cell while the player remains behind.
|
||||
_entities.ClearClipRouting();
|
||||
}
|
||||
|
||||
private void UseOutdoorPortalViewRouting(ClipViewSlice slice) =>
|
||||
_entities.SetClipRouting(
|
||||
_noCellClipRouting,
|
||||
outdoorSlot: slice.Slot,
|
||||
outdoorVisible: true);
|
||||
|
||||
public void PrepareCellBatches(
|
||||
RetailPViewFrameInput frame,
|
||||
HashSet<uint> visibleCellIds) =>
|
||||
|
|
@ -376,6 +403,7 @@ internal sealed class RetailPViewPassExecutor :
|
|||
|
||||
if (scissor)
|
||||
_surface.EndScissor();
|
||||
_entities.ClearClipRouting();
|
||||
DisableClipDistances();
|
||||
}
|
||||
|
||||
|
|
@ -418,8 +446,7 @@ internal sealed class RetailPViewPassExecutor :
|
|||
|
||||
_particleClassifications.ReplaceOutdoor(context.ParticleOwnerIds);
|
||||
|
||||
if (!frame.RootCell.IsOutdoorNode
|
||||
&& _particleClassifications.Outdoor.Count > 0
|
||||
if (_particleClassifications.Outdoor.Count > 0
|
||||
&& _particles is not null
|
||||
&& _particleRenderer is not null)
|
||||
{
|
||||
|
|
@ -427,7 +454,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
frame.Camera,
|
||||
frame.CameraWorldPosition,
|
||||
ParticleRenderPass.Scene,
|
||||
_particleClassifications.Outdoor);
|
||||
_particleClassifications.Outdoor,
|
||||
clipSlot: (uint)context.Slice.Slot);
|
||||
}
|
||||
|
||||
EnableClipDistances();
|
||||
|
|
@ -456,6 +484,77 @@ internal sealed class RetailPViewPassExecutor :
|
|||
|
||||
if (scissor)
|
||||
_surface.EndScissor();
|
||||
_entities.ClearClipRouting();
|
||||
DisableClipDistances();
|
||||
}
|
||||
|
||||
public void DrawLandscapeStaticParticles(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeStaticParticleContext context)
|
||||
{
|
||||
bool scissor = BeginDoorwayScissor(context.Slice.NdcAabb);
|
||||
_surface.BindTerrainClip();
|
||||
DisableClipDistances();
|
||||
|
||||
_particleClassifications.ReplaceOutdoor(context.ParticleOwnerIds);
|
||||
if (_particleClassifications.Outdoor.Count > 0
|
||||
&& _particles is not null
|
||||
&& _particleRenderer is not null)
|
||||
{
|
||||
_particleRenderer.DrawForOwners(
|
||||
frame.Camera,
|
||||
frame.CameraWorldPosition,
|
||||
ParticleRenderPass.Scene,
|
||||
_particleClassifications.Outdoor,
|
||||
clipSlot: (uint)context.Slice.Slot);
|
||||
}
|
||||
|
||||
if (scissor)
|
||||
_surface.EndScissor();
|
||||
_entities.ClearClipRouting();
|
||||
DisableClipDistances();
|
||||
}
|
||||
|
||||
public void DrawLandscapeBuildingShellSlice(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeBuildingShellSliceContext context)
|
||||
{
|
||||
UseOutdoorPortalViewRouting(context.Slice);
|
||||
bool scissor = BeginDoorwayScissor(context.Slice.NdcAabb);
|
||||
_surface.BindTerrainClip();
|
||||
DisableClipDistances();
|
||||
|
||||
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
|
||||
{
|
||||
RenderFrameView drawView = request.View;
|
||||
_entities.DrawPackedProductionRoute(
|
||||
frame.Camera,
|
||||
in drawView,
|
||||
request.Route,
|
||||
request.RouteIndex,
|
||||
request.CellId,
|
||||
request.TupleLandblockId);
|
||||
}
|
||||
else if (context.BuildingShells.Count > 0)
|
||||
{
|
||||
var buildingEntry = (
|
||||
frame.PlayerLandblockId ?? 0u,
|
||||
Vector3.Zero,
|
||||
Vector3.Zero,
|
||||
context.BuildingShells,
|
||||
(IReadOnlyDictionary<uint, WorldEntity>?)null);
|
||||
_entities.Draw(
|
||||
frame.Camera,
|
||||
new[] { buildingEntry },
|
||||
frame.Frustum,
|
||||
neverCullLandblockId: frame.PlayerLandblockId,
|
||||
visibleCellIds: null,
|
||||
animatedEntityIds: frame.AnimatedEntityIds);
|
||||
}
|
||||
|
||||
if (scissor)
|
||||
_surface.EndScissor();
|
||||
_entities.ClearClipRouting();
|
||||
DisableClipDistances();
|
||||
}
|
||||
|
||||
|
|
@ -468,10 +567,13 @@ internal sealed class RetailPViewPassExecutor :
|
|||
|
||||
public void DrawLookInPortalPunch(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewCellSliceContext context) =>
|
||||
DrawPortalDepthWrite(context, frame, forceFarZ: true);
|
||||
RetailPViewCellSliceContext context,
|
||||
int portalIndex) =>
|
||||
DrawPortalDepthWrite(context, frame, forceFarZ: true, portalIndex);
|
||||
|
||||
public void DrawUnattachedSceneParticles(RetailPViewFrameInput frame)
|
||||
public void DrawUnattachedSceneParticles(
|
||||
RetailPViewFrameInput frame,
|
||||
ClipViewSlice slice)
|
||||
{
|
||||
if (_particles is null || _particleRenderer is null)
|
||||
return;
|
||||
|
|
@ -482,7 +584,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
frame.CameraWorldPosition,
|
||||
ParticleRenderPass.Scene,
|
||||
_noSceneParticleEntityIds,
|
||||
includeUnattached: true);
|
||||
includeUnattached: true,
|
||||
clipSlot: (uint)slice.Slot);
|
||||
}
|
||||
|
||||
public void FlushLandscapeAlpha() => _alpha.Flush();
|
||||
|
|
@ -509,7 +612,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
frame.Camera,
|
||||
frame.CameraWorldPosition,
|
||||
ParticleRenderPass.Scene,
|
||||
visible);
|
||||
visible,
|
||||
clipSlot: (uint)context.Slice.Slot);
|
||||
DisableClipDistances();
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +656,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
private void DrawPortalDepthWrite(
|
||||
RetailPViewCellSliceContext context,
|
||||
RetailPViewFrameInput frame,
|
||||
bool forceFarZ)
|
||||
bool forceFarZ,
|
||||
int? onlyPortalIndex = null)
|
||||
{
|
||||
// Retail D3DPolyRender::DrawPortalPolyInternal @ 0x0059BC90.
|
||||
// Main interior roots stamp true depth (seal); outdoor and look-in
|
||||
|
|
@ -566,6 +671,8 @@ internal sealed class RetailPViewPassExecutor :
|
|||
Span<Vector3> world = stackalloc Vector3[32];
|
||||
for (int index = 0; index < cell.Portals.Count; index++)
|
||||
{
|
||||
if (onlyPortalIndex.HasValue && index != onlyPortalIndex.Value)
|
||||
continue;
|
||||
if (cell.Portals[index].OtherCellId != 0xFFFF)
|
||||
continue;
|
||||
if (index >= cell.PortalPolygons.Count)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue