#region usings using System; using System.Collections.Generic; using System.Linq; using OpenTK; using OpenTK.Graphics.OpenGL4; #endregion namespace SM.OGL.Framebuffer { /// /// Represents a OpenGL Framebuffer. /// public class Framebuffer : GLObject { static Framebuffer CurrentlyActiveFramebuffer; static Framebuffer CurrentlyActiveDrawFramebuffer; static Framebuffer CurrentlyActiveReadFramebuffer; /// protected override bool AutoCompile { get; set; } = true; /// /// The window for the screen /// public static IFramebufferWindow ScreenWindow; /// /// Represents the screen buffer. /// public static readonly Framebuffer Screen = new Framebuffer() { _id = 0, CanCompile = false, _window = ScreenWindow, _windowScale = 1, DefaultApplyViewport = false }; private IFramebufferWindow _window; private float _windowScale; /// public override ObjectLabelIdentifier TypeIdentifier { get; } = ObjectLabelIdentifier.Framebuffer; /// /// Contains the size of the framebuffer. /// public Vector2 Size { get; private set; } /// /// Says what value the dafault for the "applyViewport"-value in . /// public bool DefaultApplyViewport { get; set; } = true; /// /// Stores the first color attachment added. /// public ColorAttachment FirstColorAttachment { get; private set; } /// /// Contains all color attachments. /// public Dictionary ColorAttachments { get; private set; } = new Dictionary(); /// /// Contains the current renderbuffer attachments of the framebuffer. /// public List RenderbufferAttachments { get; } = new List(); /// /// Gets the color attachment with the specified color name. /// /// /// public ColorAttachment this[string colorName] => ColorAttachments[colorName]; /// /// Creates a buffer without any options. /// public Framebuffer() { } /// /// Creates a buffer, while always respecting the screen. /// /// /// public Framebuffer(IFramebufferWindow window, float scale = 1) : this(new Vector2(window.Width * scale, window.Height * scale)) { _window = window; _windowScale = scale; } /// /// Creates a buffer, with a specified size. /// /// public Framebuffer(Vector2 size) { Size = size; } /// public override void Compile() { if (_id == 0) _window = ScreenWindow; if (_window != null) Size = new Vector2(_window.Width * _windowScale, _window.Height * _windowScale); base.Compile(); _id = GL.GenFramebuffer(); GL.BindFramebuffer(FramebufferTarget.Framebuffer, _id); var enums = new DrawBuffersEnum[ColorAttachments.Count]; var c = 0; foreach (var pair in ColorAttachments) { pair.Value.Generate(this); enums[c++] = pair.Value.DrawBuffersEnum; } GL.DrawBuffers(enums.Length, enums); foreach (var pair in ColorAttachments) GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, pair.Value.FramebufferAttachment, pair.Value.Target, pair.Value.ID, 0); FramebufferErrorCode err; err = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); if (err != FramebufferErrorCode.FramebufferComplete) throw new Exception("Failed loading framebuffer.\nProblem: " + err); foreach (RenderbufferAttachment attachment in RenderbufferAttachments) { attachment.Generate(this); GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, attachment.FramebufferAttachment, RenderbufferTarget.Renderbuffer, attachment.ID); } err = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); if (err != FramebufferErrorCode.FramebufferComplete) throw new Exception("Failed loading framebuffer.\nProblem: " + err); GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); GL.BindTexture(TextureTarget.Texture2D, 0); } /// /// Disposes and clears the attachment /// public void Reset() { Dispose(); ColorAttachments.Clear(); RenderbufferAttachments.Clear(); } /// public override void Dispose() { foreach (var attachment in ColorAttachments.Values) attachment.Dispose(); FirstColorAttachment = null; foreach (RenderbufferAttachment pair in RenderbufferAttachments.ToArray()) { GL.DeleteRenderbuffer(pair.ID); } GL.DeleteFramebuffer(this); base.Dispose(); } /// /// Appends a color attachment. /// public void Append(string key, int pos) => Append(key, new ColorAttachment(pos, null)); /// /// Appends a color attachment. /// /// /// /// public void Append(string key, Vector2 size, int pos) => Append(key, new ColorAttachment(pos, size)); /// /// Appends a color attachment. /// public void Append(string key, ColorAttachment value) { if (ColorAttachments.Count == 0) FirstColorAttachment = value; ColorAttachments.Add(key, value); } /// /// Appends a renderbuffer attachment to the framebuffer. /// /// public void AppendRenderbuffer(RenderbufferAttachment attachment) { if (RenderbufferAttachments.Contains(attachment)) return; RenderbufferAttachments.Add(attachment); } /// /// Activates the framebuffer without clearing the buffer. /// public void Activate(bool? applyViewport = null) { Activate(FramebufferTarget.Framebuffer, ClearBufferMask.None, applyViewport); } /// /// Activates the framebuffer for the specific target framebuffer and without clearing. /// /// /// public void Activate(FramebufferTarget target, bool? applyViewport = null) { Activate(target, ClearBufferMask.None, applyViewport); } /// /// Activates the framebuffer while clearing the specified buffer. /// /// /// public void Activate(ClearBufferMask clearMask, bool? applyViewport = null) { Activate(FramebufferTarget.Framebuffer, clearMask, applyViewport); } /// /// Activates the framebuffer for the specific target and with clearing. /// /// /// /// public void Activate(FramebufferTarget target, ClearBufferMask clear, bool? applyViewport = null) { switch (target) { case FramebufferTarget.ReadFramebuffer: CurrentlyActiveReadFramebuffer = this; break; case FramebufferTarget.DrawFramebuffer: CurrentlyActiveDrawFramebuffer = this; break; case FramebufferTarget.Framebuffer: CurrentlyActiveFramebuffer = this; break; } GL.BindFramebuffer(target, this); if (applyViewport.GetValueOrDefault(DefaultApplyViewport)) GL.Viewport(0, 0, (int)Size.X, (int)Size.Y); GL.Clear(clear); } /// /// Copies content to the specified Framebuffer. /// public void CopyTo(Framebuffer target, ClearBufferMask clear = ClearBufferMask.ColorBufferBit, BlitFramebufferFilter filter = BlitFramebufferFilter.Linear) { Activate(FramebufferTarget.ReadFramebuffer, false); target.Activate(FramebufferTarget.DrawFramebuffer, false); GL.BlitFramebuffer(0, 0, (int)Size.X, (int)Size.Y, 0, 0, (int)Size.X, (int)Size.Y, clear, filter); } /// /// Returns a handle of the current framebuffer. /// /// /// public static Framebuffer GetCurrentlyActive(FramebufferTarget target = FramebufferTarget.Framebuffer) { switch (target) { case FramebufferTarget.ReadFramebuffer: return CurrentlyActiveReadFramebuffer ??= getCurrentlyActive(target); case FramebufferTarget.DrawFramebuffer: return CurrentlyActiveDrawFramebuffer ??= getCurrentlyActive(target); case FramebufferTarget.Framebuffer: return CurrentlyActiveFramebuffer ??= getCurrentlyActive(target); } return null; } static Framebuffer getCurrentlyActive(FramebufferTarget target = FramebufferTarget.Framebuffer) { Framebuffer buffer = new Framebuffer() { CanCompile = false, ReportAsNotCompiled = true, DefaultApplyViewport = false }; switch (target) { case FramebufferTarget.ReadFramebuffer: GL.GetInteger(GetPName.ReadFramebufferBinding, out buffer._id); break; case FramebufferTarget.DrawFramebuffer: GL.GetInteger(GetPName.DrawFramebufferBinding, out buffer._id); break; case FramebufferTarget.Framebuffer: GL.GetInteger(GetPName.FramebufferBinding, out buffer._id); break; } return buffer; } } }