new render target class while working on the exhibit UI
[laserbrain_demo] / src / rtarg.cc
1 #include "rtarg.h"
2
3 struct RTStackItem {
4         RenderTarget *rt;
5         int saved_vp[4];
6 };
7
8 static void attach_depth_texture(Texture *tex);
9
10 #define MAX_STACK_SIZE  16
11 static RTStackItem rstack[MAX_STACK_SIZE];
12 static int rtop;
13
14 void set_render_target(RenderTarget *rt)
15 {
16         if(rt) {
17                 if(!rstack[rtop].rt) {
18                         glGetIntegerv(GL_VIEWPORT, rstack[rtop].saved_vp);
19                 }
20                 rt->bind();
21
22         } else {
23                 int *vp = rstack[rtop].saved_vp;
24                 glViewport(vp[0], vp[1], vp[2], vp[3]);
25                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
26         }
27
28         rstack[rtop].rt = rt;
29 }
30
31 RenderTarget *current_render_target()
32 {
33         return rstack[rtop].rt;
34 }
35
36 bool push_render_target(RenderTarget *rt)
37 {
38         if(!rt) {
39                 error_log("push_render_target(0) is invalid\n");
40                 return false;
41         }
42         if(rtop >= MAX_STACK_SIZE - 1) {
43                 warning_log("push_render_target: overflow\n");
44                 return false;
45         }
46         int *vp = rstack[rtop + 1].saved_vp;
47         RenderTarget *prev = current_render_target();
48
49         if(prev) {
50                 vp[0] = vp[1] = 0;
51                 vp[2] = prev->get_width();
52                 vp[3] = prev->get_height();
53         } else {
54                 memcpy(vp, rstack[rtop].saved_vp, 4 * sizeof(int));
55         }
56         rstack[++rtop].rt = rt;
57         return true;
58 }
59
60 bool pop_render_target()
61 {
62         if(rtop <= 0) {
63                 error_log("pop_render_target: undeflow\n");
64                 return false;
65         }
66
67         int *vp = rstack[rtop].saved_vp;
68         glViewport(vp[0], vp[1], vp[2], vp[3]);
69
70         if(rstack[--rtop].rt) {
71                 rstack[rtop].rt->bind();
72         } else {
73                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
74         }
75         return true;
76 }
77
78 RenderTarget::RenderTarget()
79 {
80         fbo = 0;
81         rbdepth = 0;
82         width = height = tex_width = tex_height = 0;
83         auto_vport = true;
84         rtcount = 0;
85
86         for(int i=0; i<4; i++) {
87                 tex[i] = 0;
88                 own_texture[i] = true;
89         }
90 }
91
92 RenderTarget::~RenderTarget()
93 {
94         destroy();
95 }
96
97 bool RenderTarget::create(int xsz, int ysz, unsigned int fmt, unsigned int flags)
98 {
99         return create_mrt(xsz, ysz, 1, fmt, flags);
100 }
101
102 bool RenderTarget::create_mrt(int xsz, int ysz, int num, unsigned int fmt, unsigned int flags)
103 {
104         glGenFramebuffers(1, &fbo);
105         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
106
107         width = xsz;
108         height = ysz;
109         tex_width = next_pow2(xsz);
110         tex_height = next_pow2(ysz);
111         rtcount = num;
112
113         texmat.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1);
114
115         if(flags & RT_COLOR) {
116                 for(int i=0; i<num; i++) {
117                         tex[i] = new Texture;
118                         tex[i]->create(tex_width, tex_height, TEX_2D, fmt);
119                         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D,
120                                         tex[i]->get_id(), 0);
121                         own_texture[i] = true;
122                 }
123         } else {
124                 // in case set_texture was called before create
125                 for(int i=0; i<num; i++) {
126                         if(tex[i]) {
127                                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D,
128                                                 tex[i]->get_id(), 0);
129                         }
130                 }
131         }
132
133         if(flags & (RT_DEPTH | RT_STENCIL)) {
134                 unsigned int fmt, attachment;
135
136                 glGenRenderbuffers(1, &rbdepth);
137                 glBindRenderbuffer(GL_RENDERBUFFER, rbdepth);
138
139                 switch(flags & (RT_DEPTH | RT_STENCIL)) {
140                 case RT_DEPTH:
141                         fmt = GL_DEPTH_COMPONENT24;
142                         attachment = GL_DEPTH_ATTACHMENT;
143                         break;
144
145                 case RT_STENCIL:
146                         fmt = GL_STENCIL_INDEX8;
147                         attachment = GL_STENCIL_ATTACHMENT;
148                         break;
149
150                 case RT_DEPTH | RT_STENCIL:
151                         fmt = GL_DEPTH24_STENCIL8;
152                         attachment = GL_DEPTH_STENCIL_ATTACHMENT;
153                         break;
154                 }
155
156                 glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height);
157                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbdepth);
158
159         } else if(depth) {
160                 attach_depth_texture(depth);
161         }
162
163         glBindFramebuffer(GL_FRAMEBUFFER, 0);
164         return true;
165 }
166
167 void RenderTarget::destroy()
168 {
169         for(int i=0; i<4; i++) {
170                 if(tex[i] && own_texture[i]) {
171                         delete tex[i];
172                         tex[i] = 0;
173                 }
174         }
175         if(rbdepth) {
176                 glDeleteRenderbuffers(1, &rbdepth);
177                 rbdepth = 0;
178         }
179         if(fbo) {
180                 glDeleteFramebuffers(1, &fbo);
181                 fbo = 0;
182         }
183 }
184
185 int RenderTarget::get_width() const
186 {
187         return width;
188 }
189
190 int RenderTarget::get_height() const
191 {
192         return height;
193 }
194
195 void RenderTarget::set_texture(Texture *tex, int idx)
196 {
197         if(this->tex[idx] && own_texture[idx]) {
198                 delete this->tex[idx];
199         }
200         this->tex[idx] = tex;
201         own_texture[idx] = false;
202
203         if(fbo) {
204                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
205                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, GL_TEXTURE_2D,
206                                 tex->get_id(), 0);
207                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
208         }
209 }
210
211 void RenderTarget::set_depth_texture(Texture *tex)
212 {
213         depth = tex;
214
215         if(fbo) {
216                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
217                 attach_depth_texture(tex);
218                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
219         }
220 }
221
222 Texture *RenderTarget::texture(int idx) const
223 {
224         return tex[idx];
225 }
226
227 Texture *RenderTarget::depth_texture() const
228 {
229         return depth;
230 }
231
232 void RenderTarget::set_auto_viewport(bool enable)
233 {
234         auto_vport = enable;
235 }
236
237 bool RenderTarget::auto_viewport() const
238 {
239         return auto_vport;
240 }
241
242 void RenderTarget::bind() const
243 {
244         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
245
246         if(auto_vport) {
247                 glViewport(0, 0, width, height);
248         }
249 }
250
251 const Mat4 &RenderTarget::texture_matrix() const
252 {
253         return texmat;
254 }
255
256 static void attach_depth_texture(Texture *tex)
257 {
258         unsigned int fmt = tex->get_format();
259         unsigned int attachment = 0;
260
261         switch(fmt) {
262         case GL_DEPTH_COMPONENT:
263         case GL_DEPTH_COMPONENT16:
264         case GL_DEPTH_COMPONENT24:
265         case GL_DEPTH_COMPONENT32:
266                 attachment = GL_DEPTH_ATTACHMENT;
267                 break;
268
269         case GL_STENCIL_INDEX:
270         case GL_STENCIL_INDEX1:
271         case GL_STENCIL_INDEX4:
272         case GL_STENCIL_INDEX8:
273                 attachment = GL_STENCIL_ATTACHMENT;
274                 break;
275
276         case GL_DEPTH_STENCIL:
277         case GL_DEPTH24_STENCIL8:
278         case GL_DEPTH32F_STENCIL8:
279         case GL_UNSIGNED_INT_24_8:
280                 attachment = GL_DEPTH_STENCIL_ATTACHMENT;
281                 break;
282
283         default:
284                 error_log("failed to attach depth/stencil texture: unexpected texture format: %x\n", fmt);
285                 break;
286         }
287
288         glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->get_id(), 0);
289 }