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