Move camera pointer, framebuffer resize, and retained/devtools input edges behind focused reversible owners. Preserve input priority while making shutdown deactivate callbacks before live-session retirement and retry physical detach without stranding transport teardown.
118 lines
3.7 KiB
C#
118 lines
3.7 KiB
C#
using AcDream.App.Input;
|
|
using Silk.NET.Maths;
|
|
using Silk.NET.OpenGL;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
internal interface IFramebufferViewportTarget
|
|
{
|
|
void ResizeViewport(int width, int height);
|
|
}
|
|
|
|
internal sealed class SilkFramebufferViewportTarget(GL gl)
|
|
: IFramebufferViewportTarget
|
|
{
|
|
private readonly GL _gl = gl ?? throw new ArgumentNullException(nameof(gl));
|
|
|
|
public void ResizeViewport(int width, int height) =>
|
|
_gl.Viewport(0, 0, (uint)width, (uint)height);
|
|
}
|
|
|
|
internal interface IFramebufferCameraTarget
|
|
{
|
|
void SetAspect(float aspect);
|
|
}
|
|
|
|
internal sealed class CameraFramebufferTarget(CameraController camera)
|
|
: IFramebufferCameraTarget
|
|
{
|
|
private readonly CameraController _camera = camera
|
|
?? throw new ArgumentNullException(nameof(camera));
|
|
|
|
public void SetAspect(float aspect) => _camera.SetAspect(aspect);
|
|
}
|
|
|
|
internal interface IFramebufferDevToolsTarget
|
|
{
|
|
void ResetLayout(int width, int height);
|
|
}
|
|
|
|
internal sealed class DevToolsFramebufferTarget(DevToolsFramePresenter presenter)
|
|
: IFramebufferDevToolsTarget
|
|
{
|
|
private readonly DevToolsFramePresenter _presenter = presenter
|
|
?? throw new ArgumentNullException(nameof(presenter));
|
|
|
|
public void ResetLayout(int width, int height) =>
|
|
_presenter.ResetLayout(
|
|
width,
|
|
height,
|
|
DevToolsPanelLayoutCondition.Always);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The one framebuffer-size target. Logical gameplay/UI dimensions continue
|
|
/// to come from Window.Size; this owner only applies physical framebuffer
|
|
/// viewport/aspect changes in the frozen acdream host order.
|
|
/// </summary>
|
|
internal sealed class FramebufferResizeController
|
|
{
|
|
private readonly ViewportAspectState _viewportAspect;
|
|
private IFramebufferViewportTarget? _viewport;
|
|
private IFramebufferCameraTarget? _camera;
|
|
private IFramebufferDevToolsTarget? _devTools;
|
|
|
|
public FramebufferResizeController(ViewportAspectState viewportAspect) =>
|
|
_viewportAspect = viewportAspect
|
|
?? throw new ArgumentNullException(nameof(viewportAspect));
|
|
|
|
public void BindViewport(IFramebufferViewportTarget viewport)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(viewport);
|
|
BindOnce(ref _viewport, viewport, "viewport");
|
|
}
|
|
|
|
public void BindCamera(IFramebufferCameraTarget camera)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(camera);
|
|
BindOnce(ref _camera, camera, "camera");
|
|
}
|
|
|
|
public void BindDevTools(IFramebufferDevToolsTarget devTools)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(devTools);
|
|
BindOnce(ref _devTools, devTools, "developer tools");
|
|
}
|
|
|
|
public void UnbindDevTools(IFramebufferDevToolsTarget devTools)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(devTools);
|
|
if (ReferenceEquals(_devTools, devTools))
|
|
_devTools = null;
|
|
}
|
|
|
|
public void Resize(Vector2D<int> newSize) => Resize(newSize.X, newSize.Y);
|
|
|
|
public void Resize(int width, int height)
|
|
{
|
|
if (width <= 0 || height <= 0)
|
|
return;
|
|
|
|
// Frozen order: GL viewport, shared aspect publication, camera, then
|
|
// optional ImGui layout reset. Late binding never replays an earlier
|
|
// resize transition.
|
|
_viewport?.ResizeViewport(width, height);
|
|
_viewportAspect.Update(width, height);
|
|
_camera?.SetAspect(width / (float)height);
|
|
_devTools?.ResetLayout(width, height);
|
|
}
|
|
|
|
private static void BindOnce<T>(ref T? slot, T value, string name)
|
|
where T : class
|
|
{
|
|
if (slot is not null && !ReferenceEquals(slot, value))
|
|
throw new InvalidOperationException(
|
|
$"The framebuffer {name} target is already bound.");
|
|
slot = value;
|
|
}
|
|
}
|