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