1 /*
2 * This file is part of the Colobot: Gold Edition source code
3 * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://gnu.org/licenses
18 */
19
20 #include "graphics/opengl/glframebuffer.h"
21
22 #include "common/logger.h"
23
24 namespace Gfx
25 {
26
27 // CGLFramebuffer
28
29 GLuint CGLFramebuffer::m_currentFBO = 0;
30
CGLFramebuffer(const FramebufferParams & params)31 CGLFramebuffer::CGLFramebuffer(const FramebufferParams& params)
32 : m_params(params)
33 {
34 m_fbo = 0;
35 m_colorRenderbuffer = 0;
36 m_colorTexture = 0;
37 m_depthRenderbuffer = 0;
38 m_depthTexture = 0;
39 m_width = 0;
40 m_height = 0;
41 m_depth = 0;
42 m_samples = 0;
43 }
44
Create()45 bool CGLFramebuffer::Create()
46 {
47 if (m_fbo != 0) return false;
48
49 m_width = m_params.width;
50 m_height = m_params.height;
51 m_depth = m_params.depth;
52 m_samples = m_params.samples;
53
54 glGenFramebuffers(1, &m_fbo);
55 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
56
57 // create color texture
58 if (m_params.colorAttachment == FramebufferParams::AttachmentType::Texture)
59 {
60 GLint previous;
61 glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
62
63 glGenTextures(1, &m_colorTexture);
64 glBindTexture(GL_TEXTURE_2D, m_colorTexture);
65
66 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_params.width, m_params.height,
67 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
68
69 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
70 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
71 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
72 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
73
74 glBindTexture(GL_TEXTURE_2D, previous);
75
76 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexture, 0);
77 }
78 // create color renderbuffer
79 else if (m_params.colorAttachment == FramebufferParams::AttachmentType::Renderbuffer)
80 {
81 glGenRenderbuffers(1, &m_colorRenderbuffer);
82 glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
83
84 if (m_params.samples > 1)
85 glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_params.samples,
86 GL_RGBA8, m_params.width, m_params.height);
87 else
88 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, m_params.width, m_params.height);
89
90 glBindRenderbuffer(GL_RENDERBUFFER, 0);
91
92 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
93 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorRenderbuffer);
94 }
95 else
96 {
97 glDrawBuffer(GL_NONE);
98 }
99
100 GLuint depthFormat = 0;
101
102 switch (m_params.depth)
103 {
104 case 16: depthFormat = GL_DEPTH_COMPONENT16; break;
105 case 24: depthFormat = GL_DEPTH_COMPONENT24; break;
106 case 32: depthFormat = GL_DEPTH_COMPONENT32; break;
107 default: depthFormat = GL_DEPTH_COMPONENT16; break;
108 }
109
110 // create depth texture
111 if (m_params.depthAttachment == FramebufferParams::AttachmentType::Texture)
112 {
113 GLint previous;
114 glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
115
116 glGenTextures(1, &m_depthTexture);
117 glBindTexture(GL_TEXTURE_2D, m_depthTexture);
118
119 glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, m_params.width, m_params.height, 0,
120 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
121
122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
124
125 float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
126
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
131 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
132
133 glBindTexture(GL_TEXTURE_2D, previous);
134
135 glFramebufferTexture2D(GL_FRAMEBUFFER,
136 GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);
137 }
138 // create depth renderbuffer
139 else if (m_params.depthAttachment == FramebufferParams::AttachmentType::Renderbuffer)
140 {
141 glGenRenderbuffers(1, &m_depthRenderbuffer);
142 glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
143
144 if (m_params.samples > 1)
145 glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_params.samples,
146 depthFormat, m_params.width, m_params.height);
147 else
148 glRenderbufferStorage(GL_RENDERBUFFER,
149 depthFormat, m_params.width, m_params.height);
150
151 glBindRenderbuffer(GL_RENDERBUFFER, 0);
152
153 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
154 GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderbuffer);
155 }
156
157 GLuint result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
158 if (result != GL_FRAMEBUFFER_COMPLETE)
159 {
160 GetLogger()->Error("Framebuffer incomplete: ");
161
162 switch (result)
163 {
164 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
165 GetLogger()->Error("attachment point incomplete");
166 break;
167 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
168 GetLogger()->Error("missing attachment");
169 break;
170 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
171 GetLogger()->Error("draw buffer has missing color attachments");
172 break;
173 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
174 GetLogger()->Error("read buffer has missing color attachments");
175 break;
176 case GL_FRAMEBUFFER_UNSUPPORTED:
177 GetLogger()->Error("unsupported attachment format");
178 break;
179 }
180
181 GetLogger()->Error("\n");
182
183 Destroy();
184
185 glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
186 return false;
187 }
188
189 glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
190 return true;
191 }
192
Destroy()193 void CGLFramebuffer::Destroy()
194 {
195 if (m_fbo == 0) return;
196
197 if (m_currentFBO == m_fbo)
198 glBindFramebuffer(GL_FRAMEBUFFER, 0);
199
200 glDeleteFramebuffers(1, &m_fbo);
201 m_fbo = 0;
202
203 if (m_colorRenderbuffer != 0)
204 {
205 glDeleteRenderbuffers(1, &m_colorRenderbuffer);
206 m_colorRenderbuffer = 0;
207 }
208
209 if (m_colorTexture != 0)
210 {
211 glDeleteTextures(1, &m_colorTexture);
212 m_colorTexture = 0;
213 }
214
215 if (m_depthRenderbuffer != 0)
216 {
217 glDeleteRenderbuffers(1, &m_depthRenderbuffer);
218 m_depthRenderbuffer = 0;
219 }
220
221 if (m_depthTexture != 0)
222 {
223 glDeleteTextures(1, &m_depthTexture);
224 m_depthTexture = 0;
225 }
226
227 m_width = 0;
228 m_height = 0;
229 m_depth = 0;
230 m_samples = 0;
231 }
232
IsDefault()233 bool CGLFramebuffer::IsDefault()
234 {
235 return false;
236 }
237
238 //! Returns width of buffers in this framebuffer
GetWidth()239 int CGLFramebuffer::GetWidth()
240 {
241 return m_width;
242 }
243
244 //! Returns height of buffers in this framebuffer
GetHeight()245 int CGLFramebuffer::GetHeight()
246 {
247 return m_height;
248 }
249
250 //! Returns depth size in bits
GetDepth()251 int CGLFramebuffer::GetDepth()
252 {
253 return m_depth;
254 }
255
256 //! Returns number of samples or 1 if multisampling is not supported
GetSamples()257 int CGLFramebuffer::GetSamples()
258 {
259 return m_samples;
260 }
261
262 //! Returns texture that contains color buffer or 0 if not available
GetColorTexture()263 int CGLFramebuffer::GetColorTexture()
264 {
265 return m_colorTexture;
266 }
267
268 //! Returns texture that contains depth buffer or 0 if not available
GetDepthTexture()269 int CGLFramebuffer::GetDepthTexture()
270 {
271 return m_depthTexture;
272 }
273
274 //! Binds this framebuffer to context
Bind()275 void CGLFramebuffer::Bind()
276 {
277 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
278 m_currentFBO = m_fbo;
279 }
280
281 //! Unbinds this framebuffer from context
Unbind()282 void CGLFramebuffer::Unbind()
283 {
284 glBindFramebuffer(GL_FRAMEBUFFER, 0);
285 m_currentFBO = 0;
286 }
287
CopyToScreen(int fromX,int fromY,int fromWidth,int fromHeight,int toX,int toY,int toWidth,int toHeight)288 void CGLFramebuffer::CopyToScreen(int fromX, int fromY, int fromWidth, int fromHeight,
289 int toX, int toY, int toWidth, int toHeight)
290 {
291 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
292 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
293
294 glBlitFramebuffer(fromX, fromY, fromX + fromWidth, fromY + fromHeight,
295 toX, toY, toX + toWidth, toY + toHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
296
297 glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
298 }
299
300 // CGLFramebufferEXT
301 GLuint CGLFramebufferEXT::m_currentFBO = 0;
302
CGLFramebufferEXT(const FramebufferParams & params)303 CGLFramebufferEXT::CGLFramebufferEXT(const FramebufferParams& params)
304 : m_params(params)
305 {
306 m_fbo = 0;
307 m_colorRenderbuffer = 0;
308 m_colorTexture = 0;
309 m_depthRenderbuffer = 0;
310 m_depthTexture = 0;
311 m_width = 0;
312 m_height = 0;
313 m_depth = 0;
314 m_samples = 0;
315 }
316
Create()317 bool CGLFramebufferEXT::Create()
318 {
319 if (m_fbo != 0) return false;
320
321 m_width = m_params.width;
322 m_height = m_params.height;
323 m_depth = m_params.depth;
324 m_samples = m_params.samples;
325
326 glGenFramebuffersEXT(1, &m_fbo);
327 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
328
329 // create color texture
330 if (m_params.colorAttachment == FramebufferParams::AttachmentType::Texture)
331 {
332 GLint previous;
333 glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
334
335 glGenTextures(1, &m_colorTexture);
336 glBindTexture(GL_TEXTURE_2D, m_colorTexture);
337
338 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
339 m_params.width, m_params.height, 0,
340 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
341
342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
343 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
345 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
346
347 glBindTexture(GL_TEXTURE_2D, previous);
348
349 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
350 GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_colorTexture, 0);
351 }
352 // create color renderbuffer
353 else if (m_params.colorAttachment == FramebufferParams::AttachmentType::Renderbuffer)
354 {
355 glGenRenderbuffersEXT(1, &m_colorRenderbuffer);
356 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_colorRenderbuffer);
357
358 if (m_params.samples > 1)
359 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
360 m_params.samples, GL_RGBA8, m_params.width, m_params.height);
361 else
362 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8,
363 m_params.width, m_params.height);
364
365 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
366
367 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
368 GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_colorRenderbuffer);
369 }
370 else
371 {
372 glDrawBuffer(GL_NONE);
373 }
374
375 GLuint depthFormat = 0;
376
377 switch (m_params.depth)
378 {
379 case 16: depthFormat = GL_DEPTH_COMPONENT16; break;
380 case 24: depthFormat = GL_DEPTH_COMPONENT24; break;
381 case 32: depthFormat = GL_DEPTH_COMPONENT32; break;
382 default: depthFormat = GL_DEPTH_COMPONENT16; break;
383 }
384
385 // create depth texture
386 if (m_params.depthAttachment == FramebufferParams::AttachmentType::Texture)
387 {
388 GLint previous;
389 glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
390
391 glGenTextures(1, &m_depthTexture);
392 glBindTexture(GL_TEXTURE_2D, m_depthTexture);
393
394 glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, m_params.width, m_params.height, 0,
395 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
396
397 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
398 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
399
400 float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
401
402 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
404 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
405 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
406 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
407
408 glBindTexture(GL_TEXTURE_2D, previous);
409
410 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
411 GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_depthTexture, 0);
412 }
413 // create depth renderbuffer
414 else if (m_params.depthAttachment == FramebufferParams::AttachmentType::Renderbuffer)
415 {
416 glGenRenderbuffersEXT(1, &m_depthRenderbuffer);
417 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthRenderbuffer);
418
419 if (m_params.samples > 1)
420 glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
421 m_params.samples, depthFormat, m_params.width, m_params.height);
422 else
423 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, depthFormat, m_params.width, m_params.height);
424
425 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
426
427 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
428 GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthRenderbuffer);
429 }
430
431 GLuint result = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
432 if (result != GL_FRAMEBUFFER_COMPLETE_EXT)
433 {
434 GetLogger()->Error("Framebuffer incomplete: ");
435
436 switch (result)
437 {
438 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
439 GetLogger()->Error("attachment point incomplete");
440 break;
441 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
442 GetLogger()->Error("missing attachment");
443 break;
444 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
445 GetLogger()->Error("incompatible attachment dimensions");
446 break;
447 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
448 GetLogger()->Error("draw buffer has missing color attachments");
449 break;
450 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
451 GetLogger()->Error("read buffer has missing color attachments");
452 break;
453 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
454 GetLogger()->Error("unsupported attachment format");
455 break;
456 }
457
458 Destroy();
459
460 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
461 return false;
462 }
463
464 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
465 return true;
466 }
467
Destroy()468 void CGLFramebufferEXT::Destroy()
469 {
470 if (m_fbo == 0) return;
471
472 if (m_currentFBO == m_fbo)
473 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
474
475 glDeleteFramebuffersEXT(1, &m_fbo);
476 m_fbo = 0;
477
478 if (m_colorRenderbuffer != 0)
479 {
480 glDeleteRenderbuffersEXT(1, &m_colorRenderbuffer);
481 m_colorRenderbuffer = 0;
482 }
483
484 if (m_colorTexture != 0)
485 {
486 glDeleteTextures(1, &m_colorTexture);
487 m_colorTexture = 0;
488 }
489
490 if (m_depthRenderbuffer != 0)
491 {
492 glDeleteRenderbuffersEXT(1, &m_depthRenderbuffer);
493 m_depthRenderbuffer = 0;
494 }
495
496 if (m_depthTexture != 0)
497 {
498 glDeleteTextures(1, &m_depthTexture);
499 m_depthTexture = 0;
500 }
501
502 m_width = 0;
503 m_height = 0;
504 m_depth = 0;
505 m_samples = 0;
506 }
507
IsDefault()508 bool CGLFramebufferEXT::IsDefault()
509 {
510 return false;
511 }
512
513 //! Returns width of buffers in this framebuffer
GetWidth()514 int CGLFramebufferEXT::GetWidth()
515 {
516 return m_width;
517 }
518
519 //! Returns height of buffers in this framebuffer
GetHeight()520 int CGLFramebufferEXT::GetHeight()
521 {
522 return m_height;
523 }
524
525 //! Returns depth size in bits
GetDepth()526 int CGLFramebufferEXT::GetDepth()
527 {
528 return m_depth;
529 }
530
531 //! Returns number of samples or 1 if multisampling is not supported
GetSamples()532 int CGLFramebufferEXT::GetSamples()
533 {
534 return m_samples;
535 }
536
537 //! Returns texture that contains color buffer or 0 if not available
GetColorTexture()538 int CGLFramebufferEXT::GetColorTexture()
539 {
540 return m_colorTexture;
541 }
542
543 //! Returns texture that contains depth buffer or 0 if not available
GetDepthTexture()544 int CGLFramebufferEXT::GetDepthTexture()
545 {
546 return m_depthTexture;
547 }
548
549 //! Binds this framebuffer to context
Bind()550 void CGLFramebufferEXT::Bind()
551 {
552 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
553 m_currentFBO = m_fbo;
554 }
555
556 //! Unbinds this framebuffer from context
Unbind()557 void CGLFramebufferEXT::Unbind()
558 {
559 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
560 m_currentFBO = 0;
561 }
562
CopyToScreen(int fromX,int fromY,int fromWidth,int fromHeight,int toX,int toY,int toWidth,int toHeight)563 void CGLFramebufferEXT::CopyToScreen(int fromX, int fromY, int fromWidth, int fromHeight,
564 int toX, int toY, int toWidth, int toHeight)
565 {
566 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_fbo);
567 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
568
569 glBlitFramebufferEXT(fromX, fromY, fromX + fromWidth, fromY + fromHeight,
570 toX, toY, toX + toWidth, toY + toHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
571
572 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
573 }
574
575 } // end of Gfx
576