]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Allocate textures attached to framebuffers automatically
[libs/gl.git] / source / framebuffer.cpp
1 /* $Id$
2
3 This file is part of libmspgl
4 Copyright © 2007  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include "extension.h"
9 #include "ext_framebuffer_object.h"
10 #include "framebuffer.h"
11 #include "misc.h"
12 #include "renderbuffer.h"
13 #include "texture2d.h"
14
15 using namespace std;
16
17 namespace Msp {
18 namespace GL {
19
20 Framebuffer::Framebuffer(unsigned i):
21         id(i),
22         dirty(0)
23 {
24         if(id)
25                 throw InvalidParameterValue("System framebuffer must have id 0");
26
27         int viewport[4];
28         glGetIntegerv(GL_VIEWPORT, viewport);
29         width = viewport[2];
30         height = viewport[3];
31 }
32
33 Framebuffer::Framebuffer():
34         width(0),
35         height(0),
36         dirty(0)
37 {
38         static RequireExtension _ext("GL_EXT_framebuffer_object");
39
40         glGenFramebuffersEXT(1, &id);
41 }
42
43 Framebuffer::~Framebuffer()
44 {
45         if(id)
46                 glDeleteFramebuffersEXT(1, &id);
47         if(current()==this)
48                 unbind();
49 }
50
51 void Framebuffer::update_attachment(unsigned mask) const
52 {
53         if(current()==this)
54         {
55                 GLenum color_buf = GL_NONE;
56                 bool has_depth = false;
57                 for(unsigned i=0; i<attachments.size(); ++i)
58                 {
59                         const Attachment &attch = attachments[i];
60                         if(mask&(1<<i))
61                         {
62                                 if(attch.type==GL_RENDERBUFFER_EXT)
63                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, GL_RENDERBUFFER_EXT, attch.rbuf->get_id());
64                                 else if(attch.type==GL_TEXTURE_2D)
65                                 {
66                                         static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
67                                         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
68                                 }
69                                 else
70                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, 0, 0);
71                         }
72
73                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
74                                 color_buf = attch.attachment;
75                         if(attch.attachment==DEPTH_ATTACHMENT)
76                                 has_depth = true;
77                 }
78
79                 glDrawBuffer(color_buf);
80                 glDepthMask(has_depth);
81         }
82         else
83                 dirty |= mask;
84 }
85
86 void Framebuffer::check_size()
87 {
88         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
89                 if(i->type)
90                 {
91                         if(i->type==GL_RENDERBUFFER_EXT)
92                         {
93                                 width = i->rbuf->get_width();
94                                 height = i->rbuf->get_height();
95                         }
96                         else if(i->type==GL_TEXTURE_2D)
97                         {
98                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
99                                 width = tex->get_width();
100                                 height = tex->get_height();
101                         }
102                         if(current()==this)
103                                 glViewport(0, 0, width, height);
104                         break;
105                 }
106 }
107
108 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
109 {
110         if(!id)
111                 throw InvalidState("Can't attach to system framebuffer");
112
113         unsigned i = get_attachment_index(attch);
114         attachments[i].set(rbuf);
115         update_attachment(1<<i);
116         check_size();
117 }
118
119 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
120 {
121         if(!id)
122                 throw InvalidState("Can't attach to system framebuffer");
123
124         unsigned i = get_attachment_index(attch);
125         attachments[i].set(tex, level);
126         update_attachment(1<<i);
127         check_size();
128 }
129
130 void Framebuffer::detach(FramebufferAttachment attch)
131 {
132         if(!id)
133                 throw InvalidState("Can't detach from system framebuffer");
134
135         unsigned i = get_attachment_index(attch);
136         attachments[i].clear();
137         update_attachment(1<<i);
138         check_size();
139 }
140
141 FramebufferStatus Framebuffer::check_status() const
142 {
143         Bind _bind(this, true);
144         return static_cast<FramebufferStatus>(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
145 }
146
147 void Framebuffer::clear(BufferBits bits)
148 {
149         Bind _bind(this, true);
150         glClear(bits);
151 }
152
153 void Framebuffer::bind() const
154 {
155         if(set_current(this))
156         {
157                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
158                 if(dirty)
159                 {
160                         update_attachment(dirty);
161                         dirty = 0;
162                 }
163                 if(width && height)
164                         glViewport(0, 0, width, height);
165         }
166 }
167
168 const Framebuffer *Framebuffer::current()
169 {       
170         if(!cur_obj)
171                 cur_obj = &system();
172         return cur_obj;
173 }
174
175 void Framebuffer::unbind()
176 {
177         system().bind();
178 }
179
180 Framebuffer &Framebuffer::system()
181 {
182         static Framebuffer sys_framebuf(0);
183         return sys_framebuf;
184 }
185
186 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
187 {
188         for(unsigned i=0; i<attachments.size(); ++i)
189                 if(attachments[i].attachment==attch)
190                         return i;
191         attachments.push_back(Attachment(attch));
192         return attachments.size()-1;
193 }
194
195
196 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
197         attachment(a),
198         type(0),
199         level(0)
200 { }
201
202 void Framebuffer::Attachment::set(Renderbuffer &r)
203 {
204         type = GL_RENDERBUFFER_EXT;
205         rbuf = &r;
206         level = 0;
207 }
208
209 void Framebuffer::Attachment::set(Texture &t, unsigned l)
210 {
211         type = t.get_target();
212         tex = &t;
213         level = l;
214 }
215
216 void Framebuffer::Attachment::clear()
217 {
218         type = 0;
219 }
220
221 } // namespace GL
222 } // namespace Msp