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/glutil.h"
21
22 #include "common/image.h"
23 #include "common/logger.h"
24 #include "common/make_unique.h"
25
26 #include "graphics/opengl/gl14device.h"
27 #include "graphics/opengl/gl21device.h"
28 #include "graphics/opengl/gl33device.h"
29
30 #include <SDL.h>
31 #include <physfs.h>
32 #include <cstring>
33 #include <vector>
34 #include <sstream>
35 #include <algorithm>
36
37 // Graphics module namespace
38 namespace Gfx
39 {
40
41 GLuint textureCoordinates[] = { GL_S, GL_T, GL_R, GL_Q };
42 GLuint textureCoordGen[] = { GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q };
43
InitializeGLEW()44 bool InitializeGLEW()
45 {
46 static bool glewInited = false;
47
48 if (!glewInited)
49 {
50 glewExperimental = GL_TRUE;
51
52 if (glewInit() != GLEW_OK)
53 {
54 GetLogger()->Error("GLEW initialization failed\n");
55 return false;
56 }
57
58 glewInited = true;
59 }
60
61 return true;
62 }
63
DetectFramebufferSupport()64 FramebufferSupport DetectFramebufferSupport()
65 {
66 if (GetOpenGLVersion() >= 30) return FBS_ARB;
67 if (glewIsSupported("GL_ARB_framebuffer_object")) return FBS_ARB;
68 if (glewIsSupported("GL_EXT_framebuffer_object")) return FBS_EXT;
69 return FBS_NONE;
70 }
71
CreateDevice(const DeviceConfig & config,const std::string & name)72 std::unique_ptr<CDevice> CreateDevice(const DeviceConfig &config, const std::string& name)
73 {
74 if (name == "default") return MakeUnique<CGL14Device>(config);
75 else if (name == "opengl") return MakeUnique<CGL14Device>(config);
76 else if (name == "gl14") return MakeUnique<CGL14Device>(config);
77 else if (name == "gl21") return MakeUnique<CGL21Device>(config);
78 else if (name == "gl33") return MakeUnique<CGL33Device>(config);
79 else if (name == "auto")
80 {
81 int version = GetOpenGLVersion();
82
83 if (version >= 33) return MakeUnique<CGL33Device>(config);
84 else if (version >= 21) return MakeUnique<CGL21Device>(config);
85 else return MakeUnique<CGL14Device>(config);
86 }
87
88 return nullptr;
89 }
90
GetOpenGLVersion()91 int GetOpenGLVersion()
92 {
93 int major, minor;
94
95 return GetOpenGLVersion(major, minor);
96 }
97
GetOpenGLVersion(int & major,int & minor)98 int GetOpenGLVersion(int &major, int &minor)
99 {
100 const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
101
102 sscanf(version, "%d.%d", &major, &minor);
103
104 return 10 * major + minor;
105 }
106
AreExtensionsSupported(std::string list)107 bool AreExtensionsSupported(std::string list)
108 {
109 // Extract extensions to find
110 std::vector<std::string> extensions;
111 std::stringstream stream(list);
112
113 std::string value;
114
115 while (true)
116 {
117 stream >> value;
118
119 if (stream.eof())
120 break;
121
122 extensions.push_back(value);
123 }
124
125 int version = GetOpenGLVersion();
126
127 // Use glGetString
128 if (version < 30)
129 {
130 const char* text = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
131 std::stringstream stream(text);
132
133 while (!extensions.empty())
134 {
135 stream >> value;
136
137 if (stream.eof())
138 break;
139
140 auto result = std::remove(extensions.begin(), extensions.end(), value);
141
142 if (result != extensions.end())
143 extensions.erase(result);
144 }
145 }
146 // Use glGetStringi
147 else
148 {
149 int n;
150 glGetIntegerv(GL_NUM_EXTENSIONS, &n);
151
152 for (int i = 0; i < n; i++)
153 {
154 const char* name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
155 value = std::string(name);
156
157 auto result = std::remove(extensions.begin(), extensions.end(), value);
158
159 if (result != extensions.end())
160 extensions.erase(result);
161
162 if (extensions.empty())
163 break;
164 }
165 }
166
167 // Return true if found all required extensions
168 return extensions.empty();
169 }
170
GetHardwareInfo(bool full)171 std::string GetHardwareInfo(bool full)
172 {
173 int glversion = GetOpenGLVersion();
174
175 std::stringstream result;
176
177 // basic hardware information
178 const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
179 const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
180 const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
181
182 result << "Hardware information:\n\n";
183 result << "OpenGL Version:\t\t" << version << '\n';
184 result << "Hardware Vendor:\t\t" << vendor << '\n';
185 result << "Renderer:\t\t\t" << renderer << '\n';
186
187 // GLSL version if available
188 if (glversion >= 20)
189 {
190 const char* glslVersion = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
191 result << "Shading Language Version:\t" << glslVersion << '\n';
192 }
193
194 if (!full) return result.str();
195
196 // extended hardware information
197 int value = 0;
198
199 result << "\nCapabilities:\n\n";
200
201 // texture size
202 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
203 result << "Max Texture Size:\t\t" << value << '\n';
204
205 if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
206 {
207 result << "Anisotropic filtering:\t\tsupported\n";
208
209 float level;
210 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &level);
211
212 result << " Max Level:\t\t" << static_cast<int>(level) << '\n';
213 }
214 else
215 {
216 result << "Anisotropic filtering:\t\tunsupported\n";
217 }
218
219 // multitexturing
220 if (glversion >= 13)
221 {
222 result << "Multitexturing:\t\tsupported\n";
223 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &value);
224 result << " Max Texture Units:\t\t" << value << '\n';
225
226 if (glversion >= 20)
227 {
228 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &value);
229 result << " Max Texture Image Units:\t" << value << '\n';
230 }
231 }
232 else
233 {
234 result << "Multitexturing:\t\tunsupported\n";
235 }
236
237 // FBO support
238 FramebufferSupport framebuffer = DetectFramebufferSupport();
239
240 if (framebuffer == FBS_ARB)
241 {
242 result << "Framebuffer Object:\t\tsupported\n";
243 result << " Type:\t\t\tCore/ARB\n";
244
245 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &value);
246 result << " Max Renderbuffer Size:\t" << value << '\n';
247
248 glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &value);
249 result << " Max Color Attachments:\t" << value << '\n';
250
251 result << "Multisampling:\t\tsupported\n";
252
253 glGetIntegerv(GL_MAX_SAMPLES, &value);
254 result << " Max Framebuffer Samples:\t" << value << '\n';
255 }
256 else if (framebuffer == FBS_EXT)
257 {
258 result << "Framebuffer Object:\tsupported\n";
259 result << " Type:\tEXT\n";
260
261 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &value);
262 result << " Max Renderbuffer Size:\t" << value << '\n';
263
264 glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &value);
265 result << " Max Color Attachments:\t" << value << '\n';
266
267 if (glewIsSupported("GL_EXT_framebuffer_multisample"))
268 {
269 result << "Multisampling:\tsupported\n";
270
271 glGetIntegerv(GL_MAX_SAMPLES_EXT, &value);
272 result << " Max Framebuffer Samples:\t" << value << '\n';
273 }
274 }
275 else
276 {
277 result << "Framebuffer Object:\tunsupported\n";
278 }
279
280 // VBO support
281 if (glversion >= 15)
282 {
283 result << "VBO:\t\t\tsupported (core)\n";
284 }
285 else if (glewIsSupported("GL_ARB_vertex_buffer_object"))
286 {
287 result << "VBO:\t\t\tsupported (ARB)\n";
288 }
289 else
290 {
291 result << "VBO:\t\t\tunsupported\n";
292 }
293
294 return result.str();
295 }
296
ClearGLErrors()297 int ClearGLErrors()
298 {
299 int result = 0;
300
301 while (true)
302 {
303 GLint error = glGetError();
304 if (error == GL_NO_ERROR)
305 break;
306
307 result++;
308 }
309
310 return result;
311 }
312
CheckGLErrors()313 bool CheckGLErrors()
314 {
315 GLint error = glGetError();
316 bool result = false;
317
318 while (error != GL_NO_ERROR)
319 {
320 GetLogger()->Error("OpenGL error: %d\n", error);
321
322 result = true;
323
324 error = glGetError();
325 }
326
327 return result;
328 }
329
TranslateGfxPrimitive(PrimitiveType type)330 GLenum TranslateGfxPrimitive(PrimitiveType type)
331 {
332 GLenum flag = 0;
333 switch (type)
334 {
335 case PRIMITIVE_POINTS: flag = GL_POINTS; break;
336 case PRIMITIVE_LINES: flag = GL_LINES; break;
337 case PRIMITIVE_LINE_STRIP: flag = GL_LINE_STRIP; break;
338 case PRIMITIVE_LINE_LOOP: flag = GL_LINE_LOOP; break;
339 case PRIMITIVE_TRIANGLES: flag = GL_TRIANGLES; break;
340 case PRIMITIVE_TRIANGLE_STRIP: flag = GL_TRIANGLE_STRIP; break;
341 case PRIMITIVE_TRIANGLE_FAN: flag = GL_TRIANGLE_FAN; break;
342 default: assert(false); break;
343 }
344 return flag;
345 }
346
TranslateGLCompFunc(GLenum flag)347 CompFunc TranslateGLCompFunc(GLenum flag)
348 {
349 switch (flag)
350 {
351 case GL_NEVER: return COMP_FUNC_NEVER;
352 case GL_LESS: return COMP_FUNC_LESS;
353 case GL_EQUAL: return COMP_FUNC_EQUAL;
354 case GL_NOTEQUAL: return COMP_FUNC_NOTEQUAL;
355 case GL_LEQUAL: return COMP_FUNC_LEQUAL;
356 case GL_GREATER: return COMP_FUNC_GREATER;
357 case GL_GEQUAL: return COMP_FUNC_GEQUAL;
358 case GL_ALWAYS: return COMP_FUNC_ALWAYS;
359 default: assert(false); break;
360 }
361 return COMP_FUNC_NEVER;
362 }
363
TranslateGfxCompFunc(CompFunc func)364 GLenum TranslateGfxCompFunc(CompFunc func)
365 {
366 switch (func)
367 {
368 case COMP_FUNC_NEVER: return GL_NEVER;
369 case COMP_FUNC_LESS: return GL_LESS;
370 case COMP_FUNC_EQUAL: return GL_EQUAL;
371 case COMP_FUNC_NOTEQUAL: return GL_NOTEQUAL;
372 case COMP_FUNC_LEQUAL: return GL_LEQUAL;
373 case COMP_FUNC_GREATER: return GL_GREATER;
374 case COMP_FUNC_GEQUAL: return GL_GEQUAL;
375 case COMP_FUNC_ALWAYS: return GL_ALWAYS;
376 default: assert(false); break;
377 }
378 return 0;
379 }
380
TranslateGLBlendFunc(GLenum flag)381 BlendFunc TranslateGLBlendFunc(GLenum flag)
382 {
383 switch (flag)
384 {
385 case GL_ZERO: return BLEND_ZERO;
386 case GL_ONE: return BLEND_ONE;
387 case GL_SRC_COLOR: return BLEND_SRC_COLOR;
388 case GL_ONE_MINUS_SRC_COLOR: return BLEND_INV_SRC_COLOR;
389 case GL_DST_COLOR: return BLEND_DST_COLOR;
390 case GL_ONE_MINUS_DST_COLOR: return BLEND_INV_DST_COLOR;
391 case GL_SRC_ALPHA: return BLEND_SRC_ALPHA;
392 case GL_ONE_MINUS_SRC_ALPHA: return BLEND_INV_SRC_ALPHA;
393 case GL_DST_ALPHA: return BLEND_DST_ALPHA;
394 case GL_ONE_MINUS_DST_ALPHA: return BLEND_INV_DST_ALPHA;
395 case GL_SRC_ALPHA_SATURATE: return BLEND_SRC_ALPHA_SATURATE;
396 default: assert(false); break;
397 }
398
399 return BLEND_ZERO;
400 }
401
TranslateGfxBlendFunc(BlendFunc func)402 GLenum TranslateGfxBlendFunc(BlendFunc func)
403 {
404 switch (func)
405 {
406 case BLEND_ZERO: return GL_ZERO;
407 case BLEND_ONE: return GL_ONE;
408 case BLEND_SRC_COLOR: return GL_SRC_COLOR;
409 case BLEND_INV_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
410 case BLEND_DST_COLOR: return GL_DST_COLOR;
411 case BLEND_INV_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
412 case BLEND_SRC_ALPHA: return GL_SRC_ALPHA;
413 case BLEND_INV_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
414 case BLEND_DST_ALPHA: return GL_DST_ALPHA;
415 case BLEND_INV_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
416 case BLEND_SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE;
417 default: assert(false); break;
418 }
419 return 0;
420 }
421
InPlane(Math::Vector normal,float originPlane,Math::Vector center,float radius)422 bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius)
423 {
424 float distance = originPlane + Math::DotProduct(normal, center);
425
426 if (distance < -radius)
427 return false;
428
429 return true;
430 }
431
TranslateTextureCoordinate(int index)432 GLenum TranslateTextureCoordinate(int index)
433 {
434 assert(index >= 0 && index < 4);
435
436 return textureCoordinates[index];
437 }
438
TranslateTextureCoordinateGen(int index)439 GLenum TranslateTextureCoordinateGen(int index)
440 {
441 assert(index >= 0 && index < 4);
442
443 return textureCoordGen[index];
444 }
445
TranslateType(Type type)446 GLenum TranslateType(Type type)
447 {
448 switch (type)
449 {
450 case Type::BYTE: return GL_BYTE;
451 case Type::UBYTE: return GL_UNSIGNED_BYTE;
452 case Type::SHORT: return GL_SHORT;
453 case Type::USHORT: return GL_UNSIGNED_SHORT;
454 case Type::INT: return GL_INT;
455 case Type::UINT: return GL_UNSIGNED_INT;
456 case Type::HALF: return GL_HALF_FLOAT;
457 case Type::FLOAT: return GL_FLOAT;
458 case Type::DOUBLE: return GL_DOUBLE;
459 default: return 0;
460 }
461 }
462
463 std::string lastShaderError;
464
GetLastShaderError()465 std::string GetLastShaderError()
466 {
467 return lastShaderError;
468 }
469
LoadShader(GLint type,const char * filename)470 GLint LoadShader(GLint type, const char* filename)
471 {
472 PHYSFS_file *file = PHYSFS_openRead(filename);
473 if (file == nullptr)
474 {
475 CLogger::GetInstance().Error("Cannot read shader source file\n");
476 CLogger::GetInstance().Error("Missing file \"%s\"\n", filename);
477 return 0;
478 }
479
480 GLchar source[65536];
481 GLchar *sources[] = { source };
482 int length = PHYSFS_read(file, source, 1, 65536);
483 source[length] = '\0';
484
485 PHYSFS_close(file);
486
487 GLuint shader = glCreateShader(type);
488 glShaderSource(shader, 1, const_cast<const GLchar**>(sources), nullptr);
489 glCompileShader(shader);
490
491 GLint status;
492 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
493
494 if (status != GL_TRUE)
495 {
496 GLint len;
497 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
498
499 auto message = MakeUniqueArray<GLchar>(len + 1);
500 glGetShaderInfoLog(shader, len + 1, nullptr, message.get());
501
502 GetLogger()->Error("Shader compilation error occurred!\n%s\n", message.get());
503 lastShaderError = std::string("Shader compilation error occurred!\n\n") + std::string(message.get());
504
505 glDeleteShader(shader);
506 return 0;
507 }
508
509 return shader;
510 }
511
LinkProgram(int count,GLint shaders[])512 GLint LinkProgram(int count, GLint shaders[])
513 {
514 GLint program = glCreateProgram();
515
516 for (int i = 0; i < count; i++)
517 glAttachShader(program, shaders[i]);
518
519 glLinkProgram(program);
520
521 for (int i = 0; i < count; i++)
522 glDetachShader(program, shaders[i]);
523
524 GLint status;
525 glGetProgramiv(program, GL_LINK_STATUS, &status);
526
527 if (status != GL_TRUE)
528 {
529 GLint len;
530 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
531
532 auto message = MakeUniqueArray<GLchar>(len + 1);
533 glGetProgramInfoLog(program, len + 1, nullptr, message.get());
534
535 GetLogger()->Error("Shader program linking error occurred!\n%s\n", message.get());
536 lastShaderError = std::string("Shader program linking error occurred!\n\n") + std::string(message.get());
537
538 glDeleteProgram(program);
539
540 return 0;
541 }
542
543 return program;
544 }
545
GetGLFrameBufferPixels(Math::IntPoint size)546 std::unique_ptr<CGLFrameBufferPixels> GetGLFrameBufferPixels(Math::IntPoint size)
547 {
548 auto pixels = MakeUnique<CGLFrameBufferPixels>(4 * size.x * size.y);
549
550 glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels->GetPixelsData());
551
552 GLuint* p = static_cast<GLuint*> ( pixels->GetPixelsData() );
553
554 for (int i = 0; i < size.x * size.y; ++i)
555 p[i] |= 0xFF000000;
556
557 return pixels;
558 }
559
PrepareTextureData(ImageData * imageData,TexImgFormat format)560 PreparedTextureData PrepareTextureData(ImageData* imageData, TexImgFormat format)
561 {
562 PreparedTextureData texData;
563
564 bool convert = false;
565
566 texData.sourceFormat = 0;
567
568 if (format == TEX_IMG_RGB)
569 {
570 texData.sourceFormat = GL_RGB;
571 texData.alpha = false;
572 }
573 else if (format == TEX_IMG_BGR)
574 {
575 texData.sourceFormat = GL_BGR;
576 texData.alpha = false;
577 }
578 else if (format == TEX_IMG_RGBA)
579 {
580 texData.sourceFormat = GL_RGBA;
581 texData.alpha = true;
582 }
583 else if (format == TEX_IMG_BGRA)
584 {
585 texData.sourceFormat = GL_BGRA;
586 texData.alpha = true;
587 }
588 else if (format == TEX_IMG_AUTO)
589 {
590 if (imageData->surface->format->BytesPerPixel == 4)
591 {
592 if ((imageData->surface->format->Amask == 0xFF000000) &&
593 (imageData->surface->format->Rmask == 0x00FF0000) &&
594 (imageData->surface->format->Gmask == 0x0000FF00) &&
595 (imageData->surface->format->Bmask == 0x000000FF))
596 {
597 texData.sourceFormat = GL_BGRA;
598 texData.alpha = true;
599 }
600 else if ((imageData->surface->format->Amask == 0xFF000000) &&
601 (imageData->surface->format->Bmask == 0x00FF0000) &&
602 (imageData->surface->format->Gmask == 0x0000FF00) &&
603 (imageData->surface->format->Rmask == 0x000000FF))
604 {
605 texData.sourceFormat = GL_RGBA;
606 texData.alpha = true;
607 }
608 else
609 {
610 texData.sourceFormat = GL_RGBA;
611 convert = true;
612 }
613 }
614 else if (imageData->surface->format->BytesPerPixel == 3)
615 {
616 if ((imageData->surface->format->Rmask == 0xFF0000) &&
617 (imageData->surface->format->Gmask == 0x00FF00) &&
618 (imageData->surface->format->Bmask == 0x0000FF))
619 {
620 texData.sourceFormat = GL_BGR;
621 texData.alpha = false;
622 }
623 else if ((imageData->surface->format->Bmask == 0xFF0000) &&
624 (imageData->surface->format->Gmask == 0x00FF00) &&
625 (imageData->surface->format->Rmask == 0x0000FF))
626 {
627 texData.sourceFormat = GL_RGB;
628 texData.alpha = false;
629 }
630 else
631 {
632 texData.sourceFormat = GL_RGBA;
633 convert = true;
634 }
635 }
636 else
637 {
638 GetLogger()->Error("Unknown data surface format");
639 assert(false);
640 }
641 }
642 else
643 assert(false);
644
645 texData.actualSurface = imageData->surface;
646 texData.convertedSurface = nullptr;
647
648 if (convert)
649 {
650 SDL_PixelFormat format = {};
651 format.BytesPerPixel = 4;
652 format.BitsPerPixel = 32;
653 format.Aloss = format.Bloss = format.Gloss = format.Rloss = 0;
654 format.Amask = 0xFF000000;
655 format.Ashift = 24;
656 format.Bmask = 0x00FF0000;
657 format.Bshift = 16;
658 format.Gmask = 0x0000FF00;
659 format.Gshift = 8;
660 format.Rmask = 0x000000FF;
661 format.Rshift = 0;
662 format.palette = nullptr;
663 texData.convertedSurface = SDL_ConvertSurface(imageData->surface, &format, SDL_SWSURFACE);
664 if (texData.convertedSurface != nullptr)
665 texData.actualSurface = texData.convertedSurface;
666 }
667
668 return texData;
669 }
670
671 } // namespace Gfx
672