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