Extend the non-drawing oracle through ordered PView routes, dispatcher visibility and final instance payloads, and accepted retail selection parts. Lifecycle artifacts can now referee the later shadow scene without influencing production visibility or draw decisions.
329 lines
10 KiB
C#
329 lines
10 KiB
C#
using AcDream.App.Rendering.Sky;
|
|
using AcDream.App.Rendering.Wb;
|
|
using AcDream.Core.Rendering;
|
|
using AcDream.Core.Vfx;
|
|
using AcDream.Core.World;
|
|
using Silk.NET.OpenGL;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
internal interface IWorldScenePassExecutor
|
|
{
|
|
HashSet<uint>? TerrainVisibleCellIds { get; }
|
|
|
|
void BeginFrame();
|
|
|
|
void PrepareFlatWorldClip();
|
|
|
|
void DrawFlatSky(
|
|
in WorldCameraFrame camera,
|
|
in RenderFrameFoundation foundation,
|
|
DayGroupData? activeDayGroup,
|
|
float dayFraction);
|
|
|
|
void DrawFlatTerrain(in WorldCameraFrame camera, uint? playerLandblockId);
|
|
|
|
void DrawFlatEntities(
|
|
in WorldCameraFrame camera,
|
|
IEnumerable<(uint LandblockId, System.Numerics.Vector3 AabbMin,
|
|
System.Numerics.Vector3 AabbMax,
|
|
IReadOnlyList<WorldEntity> Entities,
|
|
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)> entries,
|
|
uint? playerLandblockId,
|
|
HashSet<uint> animatedEntityIds);
|
|
|
|
string DrawPostWorldParticles(
|
|
LoadedCell? clipRoot,
|
|
ClipFrameAssembly? clipAssembly,
|
|
in WorldCameraFrame camera,
|
|
IReadOnlySet<uint> outdoorOwnerIds,
|
|
string currentSignature);
|
|
|
|
void DrawFlatWeather(
|
|
in WorldCameraFrame camera,
|
|
in RenderFrameFoundation foundation,
|
|
DayGroupData? activeDayGroup,
|
|
float dayFraction);
|
|
|
|
void DisableClipDistances();
|
|
|
|
void AbortFrame();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Concrete GL leaf for the flat-world safety path and the post-world particle
|
|
/// and weather passes. Retail PView frames remain owned by
|
|
/// <see cref="RetailPViewRenderer"/> and <see cref="RetailPViewPassExecutor"/>.
|
|
/// </summary>
|
|
internal sealed class WorldScenePassExecutor : IWorldScenePassExecutor
|
|
{
|
|
private readonly GL _gl;
|
|
private readonly IRenderFrameGlState _frameGlState;
|
|
private readonly ClipFrame _clipFrame;
|
|
private readonly WbDrawDispatcher _entities;
|
|
private readonly EnvCellRenderer _environmentCells;
|
|
private readonly TerrainModernRenderer? _terrain;
|
|
private readonly TerrainDrawDiagnosticsController _terrainDiagnostics;
|
|
private readonly SkyRenderer? _sky;
|
|
private readonly ParticleSystem? _particles;
|
|
private readonly ParticleRenderer? _particleRenderer;
|
|
private readonly HashSet<uint> _visibleParticleOwners = [];
|
|
private readonly HashSet<uint> _noExcludedParticleOwners = [];
|
|
|
|
public WorldScenePassExecutor(
|
|
GL gl,
|
|
IRenderFrameGlState frameGlState,
|
|
ClipFrame clipFrame,
|
|
WbDrawDispatcher entities,
|
|
EnvCellRenderer environmentCells,
|
|
TerrainModernRenderer? terrain,
|
|
TerrainDrawDiagnosticsController terrainDiagnostics,
|
|
SkyRenderer? sky,
|
|
ParticleSystem? particles,
|
|
ParticleRenderer? particleRenderer)
|
|
{
|
|
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
|
|
_frameGlState = frameGlState
|
|
?? throw new ArgumentNullException(nameof(frameGlState));
|
|
_clipFrame = clipFrame ?? throw new ArgumentNullException(nameof(clipFrame));
|
|
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
|
|
_environmentCells = environmentCells
|
|
?? throw new ArgumentNullException(nameof(environmentCells));
|
|
_terrain = terrain;
|
|
_terrainDiagnostics = terrainDiagnostics
|
|
?? throw new ArgumentNullException(nameof(terrainDiagnostics));
|
|
_sky = sky;
|
|
_particles = particles;
|
|
_particleRenderer = particleRenderer;
|
|
}
|
|
|
|
public HashSet<uint>? TerrainVisibleCellIds => _terrain?.VisibleCellIds;
|
|
|
|
public void BeginFrame()
|
|
{
|
|
_visibleParticleOwners.Clear();
|
|
_clipFrame.Reset();
|
|
_entities.ClearClipRouting();
|
|
_environmentCells.SetClipRouting(null);
|
|
}
|
|
|
|
public void PrepareFlatWorldClip()
|
|
{
|
|
_clipFrame.ReserveTerrainUploads(_gl, 1);
|
|
_clipFrame.UploadRegions(_gl);
|
|
TerrainClipBufferBinding terrainBinding = _clipFrame.UploadTerrainClip(_gl);
|
|
_entities.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
|
_environmentCells.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
|
_terrain?.SetClipUbo(terrainBinding);
|
|
}
|
|
|
|
public void DrawFlatSky(
|
|
in WorldCameraFrame camera,
|
|
in RenderFrameFoundation foundation,
|
|
DayGroupData? activeDayGroup,
|
|
float dayFraction)
|
|
{
|
|
_clipFrame.BindTerrainClip(_gl);
|
|
EnableClipDistances();
|
|
Exception? drawFailure = null;
|
|
try
|
|
{
|
|
_sky?.RenderSky(
|
|
camera.Camera,
|
|
camera.Position,
|
|
dayFraction,
|
|
activeDayGroup,
|
|
foundation.Sky,
|
|
foundation.EnvironOverrideActive);
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
drawFailure = error;
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
DisableClipDistances();
|
|
}
|
|
catch (Exception closeFailure) when (drawFailure is not null)
|
|
{
|
|
throw new AggregateException(
|
|
"Sky drawing failed and its clip-distance bracket could not be closed.",
|
|
drawFailure,
|
|
closeFailure);
|
|
}
|
|
}
|
|
|
|
if (_particles is not null && _particleRenderer is not null)
|
|
{
|
|
_particleRenderer.Draw(
|
|
camera.Camera,
|
|
camera.Position,
|
|
ParticleRenderPass.SkyPreScene);
|
|
}
|
|
}
|
|
|
|
public void DrawFlatTerrain(
|
|
in WorldCameraFrame camera,
|
|
uint? playerLandblockId)
|
|
{
|
|
EnableClipDistances();
|
|
_terrainDiagnostics.Begin();
|
|
_terrain?.Draw(
|
|
camera.Camera,
|
|
camera.Frustum,
|
|
neverCullLandblockId: playerLandblockId);
|
|
_terrainDiagnostics.Complete();
|
|
}
|
|
|
|
public void DrawFlatEntities(
|
|
in WorldCameraFrame camera,
|
|
IEnumerable<(uint LandblockId, System.Numerics.Vector3 AabbMin,
|
|
System.Numerics.Vector3 AabbMax,
|
|
IReadOnlyList<WorldEntity> Entities,
|
|
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)> entries,
|
|
uint? playerLandblockId,
|
|
HashSet<uint> animatedEntityIds) =>
|
|
_entities.Draw(
|
|
camera.Camera,
|
|
entries,
|
|
camera.Frustum,
|
|
neverCullLandblockId: playerLandblockId,
|
|
visibleCellIds: null,
|
|
animatedEntityIds: animatedEntityIds);
|
|
|
|
public string DrawPostWorldParticles(
|
|
LoadedCell? clipRoot,
|
|
ClipFrameAssembly? clipAssembly,
|
|
in WorldCameraFrame camera,
|
|
IReadOnlySet<uint> outdoorOwnerIds,
|
|
string currentSignature)
|
|
{
|
|
if (_particles is null || _particleRenderer is null)
|
|
return currentSignature;
|
|
|
|
if (clipRoot is null)
|
|
{
|
|
if (clipAssembly is not null)
|
|
{
|
|
_particleRenderer.DrawForOwners(
|
|
camera.Camera,
|
|
camera.Position,
|
|
ParticleRenderPass.Scene,
|
|
_visibleParticleOwners,
|
|
includeUnattached: true,
|
|
excludedAttachedOwnerIds: _noExcludedParticleOwners);
|
|
return AppendSignature(currentSignature, "filtered");
|
|
}
|
|
|
|
_particleRenderer.Draw(
|
|
camera.Camera,
|
|
camera.Position,
|
|
ParticleRenderPass.Scene);
|
|
return AppendSignature(currentSignature, "global");
|
|
}
|
|
|
|
if (clipRoot.IsOutdoorNode)
|
|
{
|
|
_particleRenderer.DrawForOwners(
|
|
camera.Camera,
|
|
camera.Position,
|
|
ParticleRenderPass.Scene,
|
|
outdoorOwnerIds,
|
|
includeUnattached: true);
|
|
return AppendSignature(currentSignature, "unattached");
|
|
}
|
|
|
|
return currentSignature;
|
|
}
|
|
|
|
public void DrawFlatWeather(
|
|
in WorldCameraFrame camera,
|
|
in RenderFrameFoundation foundation,
|
|
DayGroupData? activeDayGroup,
|
|
float dayFraction)
|
|
{
|
|
_clipFrame.BindTerrainClip(_gl);
|
|
EnableClipDistances();
|
|
Exception? drawFailure = null;
|
|
try
|
|
{
|
|
_sky?.RenderWeather(
|
|
camera.Camera,
|
|
camera.Position,
|
|
dayFraction,
|
|
activeDayGroup,
|
|
foundation.Sky,
|
|
foundation.EnvironOverrideActive);
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
drawFailure = error;
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
DisableClipDistances();
|
|
}
|
|
catch (Exception closeFailure) when (drawFailure is not null)
|
|
{
|
|
throw new AggregateException(
|
|
"Weather drawing failed and its clip-distance bracket could not be closed.",
|
|
drawFailure,
|
|
closeFailure);
|
|
}
|
|
}
|
|
|
|
if (_particles is not null && _particleRenderer is not null)
|
|
{
|
|
_particleRenderer.Draw(
|
|
camera.Camera,
|
|
camera.Position,
|
|
ParticleRenderPass.SkyPostScene);
|
|
}
|
|
}
|
|
|
|
public void DisableClipDistances()
|
|
{
|
|
for (int index = 0; index < ClipFrame.MaxPlanes; index++)
|
|
_gl.Disable(EnableCap.ClipDistance0 + index);
|
|
}
|
|
|
|
public void AbortFrame()
|
|
{
|
|
List<Exception>? failures = null;
|
|
TryAbort(_frameGlState.RestoreFrameDefaults);
|
|
TryAbort(_clipFrame.Reset);
|
|
TryAbort(_entities.ClearClipRouting);
|
|
TryAbort(_entities.AbortCurrentRenderSceneObserverFrame);
|
|
TryAbort(() => _environmentCells.SetClipRouting(null));
|
|
_visibleParticleOwners.Clear();
|
|
if (failures is { Count: > 0 })
|
|
throw new AggregateException("World scene pass abort failed.", failures);
|
|
|
|
void TryAbort(Action operation)
|
|
{
|
|
try
|
|
{
|
|
operation();
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
(failures ??= []).Add(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnableClipDistances()
|
|
{
|
|
for (int index = 0; index < ClipFrame.MaxPlanes; index++)
|
|
_gl.Enable(EnableCap.ClipDistance0 + index);
|
|
}
|
|
|
|
private static string AppendSignature(string current, string value) =>
|
|
current == "none" ? value : current + "+" + value;
|
|
}
|