rev |
line source |
nuclear@0
|
1 #include <stack>
|
nuclear@0
|
2 #include "rtarg.h"
|
nuclear@0
|
3 #include "texture.h"
|
nuclear@0
|
4 #include "logger.h"
|
nuclear@0
|
5
|
nuclear@0
|
6 RenderTarget::RenderTarget()
|
nuclear@0
|
7 {
|
nuclear@0
|
8 width = height = 0;
|
nuclear@0
|
9 fbo = 0;
|
nuclear@0
|
10 rbuf_zstencil = 0;
|
nuclear@0
|
11 color_tex = 0;
|
nuclear@0
|
12 tex_face = 0;
|
nuclear@0
|
13 tex_targ = 0;
|
nuclear@0
|
14 }
|
nuclear@0
|
15
|
nuclear@0
|
16 RenderTarget::~RenderTarget()
|
nuclear@0
|
17 {
|
nuclear@0
|
18 cleanup();
|
nuclear@0
|
19 }
|
nuclear@0
|
20
|
nuclear@0
|
21 bool RenderTarget::create(unsigned int fmt)
|
nuclear@0
|
22 {
|
nuclear@0
|
23 int vp[4];
|
nuclear@0
|
24 glGetIntegerv(GL_VIEWPORT, vp);
|
nuclear@0
|
25
|
nuclear@0
|
26 return create(vp[2] - vp[0], vp[3] - vp[1], fmt);
|
nuclear@0
|
27 }
|
nuclear@0
|
28
|
nuclear@0
|
29 bool RenderTarget::create(int width, int height, unsigned int fmt)
|
nuclear@0
|
30 {
|
nuclear@0
|
31 debug_log("RenderTarget::create(%d, %d)\n", width, height);
|
nuclear@0
|
32 cleanup();
|
nuclear@0
|
33
|
nuclear@0
|
34 tex_targ = GL_TEXTURE_2D;
|
nuclear@0
|
35 this->width = width;
|
nuclear@0
|
36 this->height = height;
|
nuclear@0
|
37 int tex_width = next_pow2(width);
|
nuclear@0
|
38 int tex_height = next_pow2(height);
|
nuclear@0
|
39
|
nuclear@0
|
40 CHECKGLERR;
|
nuclear@0
|
41 color_tex = new Texture2D;
|
nuclear@0
|
42 color_tex->create(tex_width, tex_height, fmt);
|
nuclear@0
|
43 CHECKGLERR;
|
nuclear@0
|
44 tex_face = 0;
|
nuclear@0
|
45
|
nuclear@0
|
46 glGenFramebuffers(1, &fbo);
|
nuclear@0
|
47 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
nuclear@0
|
48
|
nuclear@0
|
49 glBindTexture(GL_TEXTURE_2D, color_tex->get_id());
|
nuclear@0
|
50 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0);
|
nuclear@0
|
51 glBindTexture(GL_TEXTURE_2D, 0);
|
nuclear@0
|
52
|
nuclear@0
|
53 glGenRenderbuffers(1, &rbuf_zstencil);
|
nuclear@0
|
54 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
55 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
|
nuclear@0
|
56 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
57 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
58
|
nuclear@0
|
59 glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
nuclear@0
|
60 CHECKGLERR;
|
nuclear@0
|
61 return true;
|
nuclear@0
|
62 }
|
nuclear@0
|
63
|
nuclear@0
|
64 bool RenderTarget::create(Texture *tex, int face)
|
nuclear@0
|
65 {
|
nuclear@0
|
66 debug_log("RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0),
|
nuclear@0
|
67 tex->get_size(1), face);
|
nuclear@0
|
68
|
nuclear@0
|
69 tex_targ = GL_TEXTURE_2D;
|
nuclear@0
|
70 if(dynamic_cast<TextureCube*>(tex)) {
|
nuclear@0
|
71 if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
|
nuclear@0
|
72 tex_targ = face;
|
nuclear@0
|
73 } else if(face >= 0 && face < 6) {
|
nuclear@0
|
74 tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
|
nuclear@0
|
75 } else {
|
nuclear@0
|
76 error_log("invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face);
|
nuclear@0
|
77 return false;
|
nuclear@0
|
78 }
|
nuclear@0
|
79 }
|
nuclear@0
|
80
|
nuclear@0
|
81 cleanup();
|
nuclear@0
|
82
|
nuclear@0
|
83 width = tex->get_size(0);
|
nuclear@0
|
84 height = tex->get_size(1);
|
nuclear@0
|
85
|
nuclear@0
|
86 color_tex = tex;
|
nuclear@0
|
87
|
nuclear@0
|
88 glGenFramebuffers(1, &fbo);
|
nuclear@0
|
89 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
nuclear@0
|
90
|
nuclear@0
|
91 glBindTexture(tex_targ, color_tex->get_id());
|
nuclear@0
|
92 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
|
nuclear@0
|
93 glBindTexture(tex_targ, 0);
|
nuclear@0
|
94
|
nuclear@0
|
95 glGenRenderbuffers(1, &rbuf_zstencil);
|
nuclear@0
|
96 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
97 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
nuclear@0
|
98 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
99 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
100
|
nuclear@0
|
101 glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
nuclear@0
|
102 CHECKGLERR;
|
nuclear@0
|
103 return true;
|
nuclear@0
|
104 }
|
nuclear@0
|
105
|
nuclear@0
|
106 void RenderTarget::cleanup()
|
nuclear@0
|
107 {
|
nuclear@0
|
108 delete color_tex;
|
nuclear@0
|
109 color_tex = 0;
|
nuclear@0
|
110
|
nuclear@0
|
111 if(fbo) {
|
nuclear@0
|
112 glDeleteFramebuffers(1, &fbo);
|
nuclear@0
|
113 }
|
nuclear@0
|
114 if(rbuf_zstencil) {
|
nuclear@0
|
115 glDeleteRenderbuffers(1, &rbuf_zstencil);
|
nuclear@0
|
116 }
|
nuclear@0
|
117
|
nuclear@0
|
118 fbo = rbuf_zstencil = 0;
|
nuclear@0
|
119 width = height = 0;
|
nuclear@0
|
120 tex_face = 0;
|
nuclear@0
|
121 tex_targ = 0;
|
nuclear@0
|
122 }
|
nuclear@0
|
123
|
nuclear@0
|
124 bool RenderTarget::resize(int width, int height)
|
nuclear@0
|
125 {
|
nuclear@0
|
126 this->width = width;
|
nuclear@0
|
127 this->height = height;
|
nuclear@0
|
128 int tex_width = next_pow2(width);
|
nuclear@0
|
129 int tex_height = next_pow2(height);
|
nuclear@0
|
130
|
nuclear@0
|
131 debug_log("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height);
|
nuclear@0
|
132
|
nuclear@0
|
133 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
nuclear@0
|
134
|
nuclear@0
|
135 color_tex->create(tex_width, tex_height, color_tex->get_format());
|
nuclear@0
|
136 color_tex->bind();
|
nuclear@0
|
137 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
|
nuclear@0
|
138 glBindTexture(tex_targ, 0);
|
nuclear@0
|
139
|
nuclear@0
|
140 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
|
nuclear@0
|
141 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
|
nuclear@0
|
142
|
nuclear@0
|
143 glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
nuclear@0
|
144 return true;
|
nuclear@0
|
145 }
|
nuclear@0
|
146
|
nuclear@0
|
147 int RenderTarget::get_width() const
|
nuclear@0
|
148 {
|
nuclear@0
|
149 return width;
|
nuclear@0
|
150 }
|
nuclear@0
|
151
|
nuclear@0
|
152 int RenderTarget::get_height() const
|
nuclear@0
|
153 {
|
nuclear@0
|
154 return height;
|
nuclear@0
|
155 }
|
nuclear@0
|
156
|
nuclear@0
|
157 Texture *RenderTarget::get_texture() const
|
nuclear@0
|
158 {
|
nuclear@0
|
159 return color_tex;
|
nuclear@0
|
160 }
|
nuclear@0
|
161
|
nuclear@0
|
162 Matrix4x4 RenderTarget::get_texture_matrix() const
|
nuclear@0
|
163 {
|
nuclear@0
|
164 float sx = (float)width / (float)color_tex->get_size(0);
|
nuclear@0
|
165 float sy = (float)height / (float)color_tex->get_size(1);
|
nuclear@0
|
166
|
nuclear@0
|
167 // counting on RVO to optimize this away
|
nuclear@0
|
168 return Matrix4x4(sx, 0, 0, 0,
|
nuclear@0
|
169 0, sy, 0, 0,
|
nuclear@0
|
170 0, 0, 1, 0,
|
nuclear@0
|
171 0, 0, 0, 1);
|
nuclear@0
|
172 }
|
nuclear@0
|
173
|
nuclear@0
|
174
|
nuclear@0
|
175 static const char *fbstname[] = {
|
nuclear@0
|
176 "GL_FRAMEBUFFER_COMPLETE",
|
nuclear@0
|
177 "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
|
nuclear@0
|
178 "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
|
nuclear@0
|
179 "no such fbo error",
|
nuclear@0
|
180 "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
|
nuclear@0
|
181 "GL_FRAMEBUFFER_INCOMPLETE_FORMATS",
|
nuclear@0
|
182 "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
|
nuclear@0
|
183 "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
|
nuclear@0
|
184 "GL_FRAMEBUFFER_UNSUPPORTED"
|
nuclear@0
|
185 };
|
nuclear@0
|
186
|
nuclear@0
|
187 bool RenderTarget::check() const
|
nuclear@0
|
188 {
|
nuclear@0
|
189 bool res = true;
|
nuclear@0
|
190
|
nuclear@0
|
191 #ifndef GL_ES_VERSION_2_0
|
nuclear@0
|
192 int prev_fb = 0;
|
nuclear@0
|
193 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb);
|
nuclear@0
|
194 #endif
|
nuclear@0
|
195
|
nuclear@0
|
196 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
nuclear@0
|
197
|
nuclear@0
|
198 int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
nuclear@0
|
199 if(status != GL_FRAMEBUFFER_COMPLETE) {
|
nuclear@0
|
200 error_log("RenderTarget::check: incomplete FBO %u: %s\n", fbo,
|
nuclear@0
|
201 fbstname[status - GL_FRAMEBUFFER_COMPLETE]);
|
nuclear@0
|
202 res = false;
|
nuclear@0
|
203 goto end;
|
nuclear@0
|
204 }
|
nuclear@0
|
205
|
nuclear@0
|
206 end:
|
nuclear@0
|
207 #ifndef GL_ES_VERSION_2_0
|
nuclear@0
|
208 glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
|
nuclear@0
|
209 #endif
|
nuclear@0
|
210 return res;
|
nuclear@0
|
211 }
|
nuclear@0
|
212
|
nuclear@0
|
213 void RenderTarget::bind() const
|
nuclear@0
|
214 {
|
nuclear@0
|
215 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
nuclear@0
|
216
|
nuclear@0
|
217 glViewport(0, 0, width, height);
|
nuclear@0
|
218 }
|
nuclear@0
|
219
|
nuclear@0
|
220 struct Viewport { int vp[4]; };
|
nuclear@0
|
221 static std::stack<Viewport> vpstack;
|
nuclear@0
|
222
|
nuclear@0
|
223 void set_render_target(const RenderTarget *rtarg)
|
nuclear@0
|
224 {
|
nuclear@0
|
225 Viewport vp;
|
nuclear@0
|
226
|
nuclear@0
|
227 if(rtarg) {
|
nuclear@0
|
228 glGetIntegerv(GL_VIEWPORT, vp.vp);
|
nuclear@0
|
229 vpstack.push(vp);
|
nuclear@0
|
230
|
nuclear@0
|
231 rtarg->bind();
|
nuclear@0
|
232 } else {
|
nuclear@0
|
233 #ifdef GL_ES_VERSION_2_0
|
nuclear@0
|
234 extern unsigned int default_fbo;
|
nuclear@0
|
235 glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
|
nuclear@0
|
236 #else
|
nuclear@0
|
237 glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
nuclear@0
|
238 #endif
|
nuclear@0
|
239
|
nuclear@0
|
240 if(vpstack.empty()) {
|
nuclear@0
|
241 return;
|
nuclear@0
|
242 }
|
nuclear@0
|
243 vp = vpstack.top();
|
nuclear@0
|
244 vpstack.pop();
|
nuclear@0
|
245 glViewport(vp.vp[0], vp.vp[1], vp.vp[2], vp.vp[3]);
|
nuclear@0
|
246 }
|
nuclear@0
|
247 }
|
nuclear@0
|
248
|
nuclear@0
|
249 int next_pow2(int x)
|
nuclear@0
|
250 {
|
nuclear@0
|
251 x--;
|
nuclear@0
|
252 x = (x >> 1) | x;
|
nuclear@0
|
253 x = (x >> 2) | x;
|
nuclear@0
|
254 x = (x >> 4) | x;
|
nuclear@0
|
255 x = (x >> 8) | x;
|
nuclear@0
|
256 x = (x >> 16) | x;
|
nuclear@0
|
257 return x + 1;
|
nuclear@0
|
258 }
|
nuclear@0
|
259
|