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