]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Set all color attachments of a Framebuffer as draw buffers
[libs/gl.git] / source / framebuffer.cpp
1 #include "arb_draw_buffers.h"
2 #include "error.h"
3 #include "ext_framebuffer_blit.h"
4 #include "ext_framebuffer_object.h"
5 #include "framebuffer.h"
6 #include "misc.h"
7 #include "renderbuffer.h"
8 #include "texture2d.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Framebuffer::Framebuffer(unsigned i):
16         id(i),
17         dirty(0)
18 {
19         if(id)
20                 throw invalid_argument("System framebuffer must have id 0");
21
22         int viewport[4];
23         glGetIntegerv(GL_VIEWPORT, viewport);
24         width = viewport[2];
25         height = viewport[3];
26 }
27
28 Framebuffer::Framebuffer():
29         width(0),
30         height(0),
31         dirty(0)
32 {
33         static Require _req(EXT_framebuffer_object);
34
35         glGenFramebuffers(1, &id);
36 }
37
38 Framebuffer::~Framebuffer()
39 {
40         if(id)
41                 glDeleteFramebuffers(1, &id);
42         if(current()==this)
43                 unbind();
44 }
45
46 void Framebuffer::update_attachment(unsigned mask) const
47 {
48         if(current()==this)
49         {
50                 std::vector<GLenum> color_bufs;
51                 color_bufs.reserve(attachments.size());
52                 for(unsigned i=0; i<attachments.size(); ++i)
53                 {
54                         const Attachment &attch = attachments[i];
55                         if(mask&(1<<i))
56                         {
57                                 if(attch.type==GL_RENDERBUFFER)
58                                         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
59                                 else if(attch.type==GL_TEXTURE_2D)
60                                 {
61                                         static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
62                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
63                                 }
64                                 else if(attch.type==GL_TEXTURE_CUBE_MAP)
65                                 {
66                                         static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
67                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.cube_face, attch.tex->get_id(), attch.level);
68                                 }
69                                 else
70                                         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
71                         }
72
73                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
74                                 color_bufs.push_back(attch.attachment);
75                 }
76
77                 if(color_bufs.empty())
78                         glDrawBuffer(GL_NONE);
79                 else if(color_bufs.size()==1)
80                         glDrawBuffer(color_bufs.front());
81                 else
82                 {
83                         static Require _req(ARB_draw_buffers);
84                         glDrawBuffers(color_bufs.size(), &color_bufs[0]);
85                 }
86         }
87         else
88                 dirty |= mask;
89 }
90
91 void Framebuffer::check_size()
92 {
93         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
94                 if(i->type)
95                 {
96                         if(i->type==GL_RENDERBUFFER)
97                         {
98                                 width = i->rbuf->get_width();
99                                 height = i->rbuf->get_height();
100                         }
101                         else if(i->type==GL_TEXTURE_2D)
102                         {
103                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
104                                 width = tex->get_width();
105                                 height = tex->get_height();
106                         }
107                         else if(i->type==GL_TEXTURE_CUBE_MAP)
108                         {
109                                 width = static_cast<TextureCube *>(i->tex)->get_size();
110                                 height = width;
111                         }
112                         if(current()==this)
113                                 glViewport(0, 0, width, height);
114                         break;
115                 }
116 }
117
118 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
119 {
120         for(unsigned i=0; i<attachments.size(); ++i)
121                 if(attachments[i].attachment==attch)
122                         return i;
123         attachments.push_back(Attachment(attch));
124         return attachments.size()-1;
125 }
126
127 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
128 {
129         if(!id)
130                 throw invalid_operation("Framebuffer::attach");
131
132         unsigned i = get_attachment_index(attch);
133         attachments[i].set(rbuf);
134         update_attachment(1<<i);
135         check_size();
136 }
137
138 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
139 {
140         if(!id)
141                 throw invalid_operation("Framebuffer::attach");
142
143         unsigned i = get_attachment_index(attch);
144         attachments[i].set(tex, 0, level);
145         update_attachment(1<<i);
146         check_size();
147 }
148
149 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
150 {
151         if(!id)
152                 throw invalid_operation("Framebuffer::attach");
153
154         unsigned i = get_attachment_index(attch);
155         attachments[i].set(tex, face, level);
156         update_attachment(1<<i);
157         check_size();
158 }
159
160 void Framebuffer::detach(FramebufferAttachment attch)
161 {
162         if(!id)
163                 throw invalid_operation("Framebuffer::detach");
164
165         unsigned i = get_attachment_index(attch);
166         attachments[i].clear();
167         update_attachment(1<<i);
168         check_size();
169 }
170
171 FramebufferStatus Framebuffer::check_status() const
172 {
173         Bind _bind(this, true);
174         return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
175 }
176
177 void Framebuffer::clear(BufferBits bits)
178 {
179         Bind _bind(this, true);
180         glClear(bits);
181 }
182
183 void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter)
184 {
185         static Require _req(EXT_framebuffer_blit);
186
187         const Framebuffer *old = current();
188         if(set_current(this))
189         {
190                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
191                 if(dirty)
192                 {
193                         update_attachment(dirty);
194                         dirty = 0;
195                 }
196         }
197         if(old!=&other)
198                 glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
199
200         glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
201
202         set_current(old);
203         glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
204 }
205
206 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
207 {
208         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
209 }
210
211 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
212 {
213         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
214 }
215
216 void Framebuffer::bind() const
217 {
218         if(set_current(this))
219         {
220                 glBindFramebuffer(GL_FRAMEBUFFER, id);
221                 if(dirty)
222                 {
223                         update_attachment(dirty);
224                         dirty = 0;
225                 }
226                 if(width && height)
227                         glViewport(0, 0, width, height);
228         }
229 }
230
231 const Framebuffer *Framebuffer::current()
232 {       
233         if(!cur_obj)
234                 cur_obj = &system();
235         return cur_obj;
236 }
237
238 void Framebuffer::unbind()
239 {
240         system().bind();
241 }
242
243 Framebuffer &Framebuffer::system()
244 {
245         static Framebuffer sys_framebuf(0);
246         return sys_framebuf;
247 }
248
249
250 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
251         attachment(a),
252         type(0),
253         level(0)
254 { }
255
256 void Framebuffer::Attachment::set(Renderbuffer &r)
257 {
258         type = GL_RENDERBUFFER;
259         rbuf = &r;
260         level = 0;
261 }
262
263 void Framebuffer::Attachment::set(Texture &t, GLenum f, unsigned l)
264 {
265         type = t.get_target();
266         tex = &t;
267         cube_face = f;
268         level = l;
269 }
270
271 void Framebuffer::Attachment::clear()
272 {
273         type = 0;
274 }
275
276 } // namespace GL
277 } // namespace Msp