1 module des.gl.base.frame; 2 3 import std.exception; 4 import std.conv; 5 6 import derelict.opengl3.gl3; 7 8 import des.math.linear; 9 10 import des.gl.base.texture; 11 import des.gl.base.type; 12 import des.gl.base.rbo; 13 14 import des.il.image; 15 16 /// 17 class GLFBOException : DesGLException 18 { 19 /// 20 this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 21 { super( msg, file, line ); } 22 } 23 24 /// 25 class GLFrameBuffer : DesObject 26 { 27 mixin DES; 28 mixin ClassLogger; 29 30 protected: 31 uint _id; 32 static uint[] id_stack; 33 34 /// 35 enum Attachment 36 { 37 COLOR = GL_COLOR_ATTACHMENT0, /// `GL_COLOR_ATTACHMENT0` 38 DEPTH = GL_DEPTH_ATTACHMENT, /// `GL_DEPTH_ATTACHMENT` 39 STENCIL = GL_STENCIL_ATTACHMENT, /// `GL_STENCIL_ATTACHMENT` 40 DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT, /// `GL_DEPTH_STENCIL_ATTACHMENT` 41 } 42 43 public: 44 45 /// `glGenFramebuffers` 46 this() 47 { 48 if( id_stack.length == 0 ) id_stack ~= 0; 49 50 checkGLCall!glGenFramebuffers( 1, &_id ); 51 logger = new InstanceLogger( this, format( "%d", _id ) ); 52 logger.Debug( "pass" ); 53 } 54 55 final pure const @property 56 { 57 /// 58 uint id() { return _id; } 59 } 60 61 final nothrow 62 { 63 /// `glBindFramebuffer` add id to stack 64 void bind() 65 { 66 if( id_stack[$-1] == _id ) return; 67 ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, _id ); 68 id_stack ~= _id; 69 debug logger.trace( "pass" ); 70 } 71 72 /// pop from stack old frame buffer id and `glBindFramebuffer` with it 73 void unbind() 74 { 75 if( id_stack.length < 2 && id_stack[$-1] != _id ) return; 76 id_stack.length--; 77 ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, id_stack[$-1] ); 78 debug logger.trace( "bind [%d]", id_stack[$-1] ); 79 } 80 } 81 82 /// 83 void drawBuffers( in int[] bufs... ) 84 { 85 int max_bufs; 86 checkGLCall!glGetIntegerv( GL_MAX_DRAW_BUFFERS, &max_bufs ); 87 enforce( bufs.length < max_bufs, 88 new GLFBOException( format( "count of draw buffers greater what max value (%d>%d)", bufs.length, max_bufs ) ) ); 89 bind(); scope(exit) unbind(); 90 GLenum[] res; 91 foreach( val; bufs ) 92 if( val < 0 ) res ~= GL_NONE; 93 else res ~= cast(GLenum)( Attachment.COLOR + val ); 94 checkGLCall!glDrawBuffers( cast(int)res.length, res.ptr ); 95 } 96 97 /// set render buffer as depth attachment 98 void setRBODepth( GLRenderBuffer rbo ) 99 in{ assert( rbo !is null ); } body 100 { 101 bind(); scope(exit) unbind(); 102 setRBO( rbo, Attachment.DEPTH ); 103 logger.Debug( "[%d]", rbo.id ); 104 } 105 106 /// set render buffer as color attachment 107 void setRBOColor( GLRenderBuffer rbo, uint no ) 108 in{ assert( rbo !is null ); } body 109 { 110 bind(); scope(exit) unbind(); 111 setRBO( rbo, cast(GLenum)( Attachment.COLOR + no ) ); 112 logger.Debug( "[%d] as COLOR%d", rbo.id, no ); 113 } 114 115 /// set texture as depth attachment 116 void setTexDepth( GLTexture tex ) 117 { 118 bind(); scope(exit) unbind(); 119 setTex( tex, Attachment.DEPTH ); 120 logger.Debug( "[%d]", tex.id ); 121 } 122 123 /// set texture as color attachment 124 void setTexColor( GLTexture tex, uint no=0 ) 125 { 126 bind(); scope(exit) unbind(); 127 setTex( tex, cast(GLenum)( Attachment.COLOR + no ) ); 128 logger.Debug( "[%d] as COLOR%d", tex.id, no ); 129 } 130 131 /// set depth attachment, `T` must be `GLRenderBuffer` or `GLTexture` 132 void setDepth(T)( T buf ) 133 in{ assert( buf !is null ); } body 134 { 135 static if( is( T : GLRenderBuffer ) ) setRBODepth( buf ); 136 else static if( is( T : GLTexture ) ) setTexDepth( buf ); 137 else static assert(0, "only GLRenderBuffer or GLTexture can be setted as attachment" ); 138 } 139 140 /// set color attachment, `T` must be `GLRenderBuffer` or `GLTexture` 141 void setColor(T)( T buf, uint no=0 ) 142 in{ assert( buf !is null ); } body 143 { 144 static if( is( T : GLRenderBuffer ) ) setRBOColor( buf, no ); 145 else static if( is( T : GLTexture ) ) setTexColor( buf, no ); 146 else static assert(0, "only GLRenderBuffer or GLTexture can be setted as attachment" ); 147 } 148 149 /// `glCheckFramebufferStatus` 150 void check() 151 { 152 bind(); scope(exit) unbind(); 153 auto status = checkGLCall!glCheckFramebufferStatus( GL_FRAMEBUFFER ); 154 import std..string; 155 if( status != GL_FRAMEBUFFER_COMPLETE ) 156 throw new GLFBOException( format( "status isn't GL_FRAMEBUFFER_COMPLETE, it's %#x", status ) ); 157 logger.Debug( "pass" ); 158 } 159 160 protected: 161 162 /// warning: no bind 163 void setRBO( GLRenderBuffer rbo, GLenum attachment ) 164 in{ assert( rbo !is null ); } body 165 { 166 checkGLCall!glFramebufferRenderbuffer( GL_FRAMEBUFFER, 167 attachment, GL_RENDERBUFFER, rbo.id ); 168 } 169 170 /// warning: no bind 171 void texture1D( GLTexture tex, GLenum attachment, uint level=0 ) 172 in { assert( tex !is null ); } body 173 { 174 checkGLCall!glFramebufferTexture1D( GL_FRAMEBUFFER, attachment, 175 tex.Target.T1D, tex.id, level ); 176 } 177 178 /// warning: no bind 179 void texture2D( GLTexture tex, GLenum attachment, 180 GLTexture.Target target=GLTexture.Target.T2D, uint level=0 ) 181 in { assert( tex !is null ); } body 182 { 183 checkGLCall!glFramebufferTexture2D( GL_FRAMEBUFFER, attachment, 184 target, tex.id, level ); 185 } 186 187 /// warning: no bind 188 void texture3D( GLTexture tex, GLenum attachment, uint level=0, int layer=0 ) 189 in { assert( tex !is null ); } body 190 { 191 checkGLCall!glFramebufferTexture3D( GL_FRAMEBUFFER, attachment, 192 tex.Target.T3D, tex.id, level, layer ); 193 } 194 195 /// warning: no bind 196 void setTex( GLTexture tex, GLenum attachment, GLTexture.Target target=GLTexture.Target.T2D ) 197 { 198 if( tex.target == tex.Target.T1D ) 199 texture1D( tex, attachment ); 200 else if( tex.target == tex.Target.T3D ) 201 texture3D( tex, attachment ); 202 else 203 texture2D( tex, attachment, target ); 204 } 205 206 override void selfDestroy() 207 { 208 unbind(); 209 checkGLCall!glDeleteFramebuffers( 1, &_id ); 210 logger.Debug( "pass" ); 211 } 212 }