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