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