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? 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 Entities, IReadOnlyDictionary? AnimatedById)> entries, uint? playerLandblockId, HashSet animatedEntityIds); string DrawPostWorldParticles( LoadedCell? clipRoot, ClipFrameAssembly? clipAssembly, in WorldCameraFrame camera, IReadOnlySet outdoorOwnerIds, string currentSignature); void DrawFlatWeather( in WorldCameraFrame camera, in RenderFrameFoundation foundation, DayGroupData? activeDayGroup, float dayFraction); void DisableClipDistances(); void AbortFrame(); } /// /// Concrete GL leaf for the flat-world safety path and the post-world particle /// and weather passes. Retail PView frames remain owned by /// and . /// 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 _visibleParticleOwners = []; private readonly HashSet _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? 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 Entities, IReadOnlyDictionary? AnimatedById)> entries, uint? playerLandblockId, HashSet 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 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? failures = null; TryAbort(_frameGlState.RestoreFrameDefaults); TryAbort(_clipFrame.Reset); TryAbort(_entities.ClearClipRouting); 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; }