1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::prelude::*;
use crate::data::Texture2D;
use crate::data::Renderbuffer;
use crate::{Context, GLContext};


type FramebufferResource = <glow::Context as HasContext>::Framebuffer;

enum FramebufferAttachment {
    Texture(Texture2D),
    Renderbuffer(Renderbuffer),
    None
}

/// A Framebuffer representation with optional `color`, `depth` and `stencil` attachments.
pub struct Framebuffer {
    gl         : GLContext,
    resource   : FramebufferResource,
    dimensions : (usize, usize),
    color      : FramebufferAttachment,
    _depth     : FramebufferAttachment,
    _stencil   : FramebufferAttachment
}

//FIXME: Incomplete implementation
// 1. Lacks default renderbuffers for depth and stencil testing
// 2. Lacks depth and stencil implementation for textures
// 3. Lacks framebuffer completeness test
// 4. Lacks checking for returning Result::Err
// 5. Check attachment dimensions (does framebuffer completeness check takes that into account?)

impl Framebuffer {
    /// The default `Framebuffer` created during the `Context` creation.
    pub fn default(context:&Context) -> Self {
        let dimensions = context.inner_dimensions();
        let resource   = Default::default();
        let color      = FramebufferAttachment::Renderbuffer(Renderbuffer::default(context));
        let _depth     = FramebufferAttachment::Renderbuffer(Renderbuffer::default(context));
        let _stencil   = FramebufferAttachment::Renderbuffer(Renderbuffer::default(context));
        let gl         = context.gl_context();
        Self { gl, resource, dimensions, color, _depth, _stencil }
    }

    pub(crate) fn resource(&self) -> FramebufferResource {
        self.resource
    }

    pub(crate) fn bind(&self) {
        let gl       = &self.gl;
        let resource = self.resource();
        let resource = if resource == Default::default() { None } else { Some(resource) };
        unsafe {
            gl.bind_framebuffer(glow::FRAMEBUFFER, resource);
        }
    }

    /// Creates a new `Framebuffer` with optional `color`, `depth` and `stencil`.
    pub fn new
    (context:&Context, color: Option<Texture2D>,
     depth:Option<Texture2D>, stencil:Option<Texture2D>) -> Result<Self,
        String> {
        let gl       = context.gl_context();
        let resource = unsafe {
            let resource = gl.create_framebuffer().expect("Couldn't create Framebuffer");
            gl.bind_framebuffer(glow::FRAMEBUFFER, Some(resource));
            resource
        };
        let mut dimensions = (0, 0);

        let color = match color {
            Some(texture) => {
                dimensions = texture.dimensions();
                unsafe {
                    gl.framebuffer_texture_2d(glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0,
                                              glow::TEXTURE_2D, Some(texture.resource()), 0);
                }
                FramebufferAttachment::Texture(texture)
            },
            None => FramebufferAttachment::None
        };
        let _depth = match depth {
            Some(texture) => FramebufferAttachment::Texture(texture),
            None => FramebufferAttachment::None
        };
        let _stencil = match stencil {
            Some(texture) => FramebufferAttachment::Texture(texture),
            None => FramebufferAttachment::None
        };

        Ok(Self {gl, resource, dimensions, color, _depth, _stencil})
    }

    /// Gets the `Framebuffer`'s dimension.
    pub fn dimensions(&self) -> (usize, usize) { self.dimensions }

    /// Returns the `Texture2D` used as the `ColorBuffer` if any.
    pub fn color(&self) -> Option<&Texture2D> {
        match &self.color {
            FramebufferAttachment::Texture(texture) => Some(&texture),
            _ => None
        }
    }
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            self.gl.delete_framebuffer(self.resource());
        }
    }
}