/************************************************************************************ AstroMenace Hardcore 3D space scroll-shooter with spaceship upgrade possibilities. Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard AstroMenace is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. AstroMenace is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AstroMenace. If not, see . Website: https://viewizard.com/ Project: https://github.com/viewizard/astromenace E-mail: viewizard@viewizard.com *************************************************************************************/ // NOTE SDL2 could be used for OpenGL context setup. "request" OpenGL context version: // SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); // SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // NOTE glGetStringi() for GL_EXTENSIONS (since OpenGL 3.0) // glGetString() usage with GL_EXTENSIONS is deprecated // // int NumberOfExtensions; // glGetIntegerv(GL_NUM_EXTENSIONS, &NumberOfExtensions); // for (int i = 0; i < NumberOfExtensions; i++) { // const GLubyte *one_string = glGetStringi(GL_EXTENSIONS, i); // } // NOTE GL_MAX_TEXTURE_MAX_ANISOTROPY (since OpenGL 4.6) // could be used to replace GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT #include "graphics_internal.h" #include "graphics.h" #include "extensions.h" namespace viewizard { namespace { // hardware device capabilities sDevCaps DevCaps{}; // for 2D we need fixed internal resolution, since window's size could be // different and we should guaranty, that 2D looks same for all window's sizes, // it is important to understand, that we don't use this values directly, // but only for 'mapping' internal window coordinates into real window coordinates float InternalWidth{0.0f}; float InternalHeight{0.0f}; bool InternalResolution{false}; // pointer to main window structure SDL_Window *SDLWindow{nullptr}; // pointer to OpenGL context SDL_GLContext GLContext{nullptr}; // main FBO std::shared_ptr MainFBO{}; // resolve FBO (for blit main FBO with multisample) std::shared_ptr ResolveFBO{}; } // unnamed namespace /* * Get SDL window handle. */ SDL_Window *vw_GetSDLWindow() { assert(SDLWindow); return SDLWindow; } /* * Check supported OpenGL extension. */ static bool ExtensionSupported(const char *Extension) { char *extensions; extensions = (char *) glGetString(GL_EXTENSIONS); // WARNING fix conversion if (strstr(extensions, Extension) != nullptr) return true; return false; } /* * Create window. */ bool vw_CreateWindow(const char *Title, int Width, int Height, bool Fullscreen, int DisplayIndex) { Uint32 Flags{SDL_WINDOW_OPENGL}; if (Fullscreen) Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDLWindow = SDL_CreateWindow(Title, SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayIndex), Width, Height, Flags); if (!SDLWindow) { std::cerr << __func__ << "(): " << "SDL_CreateWindow() failed: " << SDL_GetError() << "\n"; std::cerr << __func__ << "(): " << "Can't set video mode " << Width << " x " << Height << "\n\n"; return false; } if (Fullscreen) std::cout << "Fullscreen mode: "; else std::cout << "Windowed mode: "; std::cout << Width << " x " << Height << "\n\n"; SDL_DisableScreenSaver(); return true; } /* * Destroy window. */ void vw_DestroyWindow() { if (!SDLWindow) return; SDL_DestroyWindow(SDLWindow); SDLWindow = nullptr; } /* * Create OpenGL context. */ bool vw_CreateOpenGLContext(int VSync) { if (!SDLWindow) { std::cerr << __func__ << "(): " << "Can't create OpenGL context, create window first.\n"; return false; } GLContext = SDL_GL_CreateContext(SDLWindow); if (!GLContext) { std::cerr << __func__ << "(): " << "SDL_GL_CreateContext() failed: " << SDL_GetError() << "\n"; std::cerr << __func__ << "(): " << "Can't create OpenGL context.\n"; return false; } if (SDL_GL_SetSwapInterval(VSync) == -1) std::cerr << __func__ << "(): " << "SDL_GL_SetSwapInterval() failed: " << SDL_GetError() << "\n"; DevCaps.OpenGLmajorVersion = 1; DevCaps.OpenGLminorVersion = 0; DevCaps.MaxTextureWidth = 0; DevCaps.MaxTextureHeight = 0; DevCaps.MaxActiveLights = 0; DevCaps.MaxAnisotropyLevel = 0; DevCaps.FramebufferObjectDepthSize = 0; DevCaps.OpenGL_1_3_supported = Initialize_OpenGL_1_3(); DevCaps.OpenGL_1_5_supported = Initialize_OpenGL_1_5(); DevCaps.OpenGL_2_0_supported = Initialize_OpenGL_2_0(); DevCaps.OpenGL_2_1_supported = Initialize_OpenGL_2_1(); DevCaps.OpenGL_3_0_supported = Initialize_OpenGL_3_0(); DevCaps.OpenGL_4_2_supported = Initialize_OpenGL_4_2(); Initialize_GL_NV_framebuffer_multisample_coverage(); // we don't have it in DevCaps, this is 1 function check only DevCaps.EXT_texture_compression_s3tc = ExtensionSupported("GL_EXT_texture_compression_s3tc"); DevCaps.ARB_texture_compression_bptc = ExtensionSupported("GL_ARB_texture_compression_bptc"); DevCaps.ARB_texture_non_power_of_two = ExtensionSupported("GL_ARB_texture_non_power_of_two"); DevCaps.SGIS_generate_mipmap = ExtensionSupported("GL_SGIS_generate_mipmap"); if (ExtensionSupported("GL_EXT_texture_filter_anisotropic")) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &DevCaps.MaxAnisotropyLevel); std::cout << "Max anisotropy: " << DevCaps.MaxAnisotropyLevel << "\n"; } std::cout << "Vendor : " << glGetString(GL_VENDOR) << "\n"; std::cout << "Renderer : " << glGetString(GL_RENDERER) << "\n"; std::cout << "Version : " << glGetString(GL_VERSION) << "\n"; glGetIntegerv(GL_MAJOR_VERSION, &DevCaps.OpenGLmajorVersion); glGetIntegerv(GL_MINOR_VERSION, &DevCaps.OpenGLminorVersion); glGetError(); // reset errors std::cout << "OpenGL Version : " << DevCaps.OpenGLmajorVersion << "." << DevCaps.OpenGLminorVersion << "\n\n"; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &DevCaps.MaxTextureHeight); std::cout << "Max texture height: " << DevCaps.MaxTextureHeight << "\n"; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &DevCaps.MaxTextureWidth); std::cout << "Max texture width: " << DevCaps.MaxTextureWidth << "\n"; glGetIntegerv(GL_MAX_LIGHTS, &DevCaps.MaxActiveLights); std::cout << "Max lights: " << DevCaps.MaxActiveLights << "\n"; // since we support FBO, should check supported samples if (DevCaps.OpenGL_3_0_supported) { int MaxSamples{0}; glGetIntegerv(GL_MAX_SAMPLES_EXT, &MaxSamples); std::cout << "Max Samples: " << MaxSamples << "\n"; // MSAA for (int i = 2; i <= MaxSamples; i *= 2) { DevCaps.MultisampleCoverageModes.emplace_back(i, i); } // CSAA if (ExtensionSupported("GL_NV_framebuffer_multisample_coverage")) { int NumModes{0}; glGetIntegerv(GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV, &NumModes); std::cout << "Coverage modes: " << NumModes << "\n"; std::vector modes(NumModes * 2); glGetIntegerv(GL_MULTISAMPLE_COVERAGE_MODES_NV, modes.data()); // fill MultisampleCoverageModes with MSAA/CSAA modes DevCaps.MultisampleCoverageModes.clear(); for (int i = 0; i < (NumModes * 2); i += 2) { DevCaps.MultisampleCoverageModes.emplace_back(modes[i + 1], modes[i]); } } } #ifndef NDEBUG // print all supported OpenGL extensions (one per line) if (glGetString(GL_EXTENSIONS) != nullptr) { std::string extensions{(char *)glGetString(GL_EXTENSIONS)}; // WARNING fix conversion if (!extensions.empty()) { std::replace(extensions.begin(), extensions.end(), ' ', '\n'); // replace all ' ' to '\n' std::cout << "Supported OpenGL extensions:\n" << extensions << "\n"; } } #endif // NDEBUG std::cout << "\n"; return true; } /* * Delete OpenGL context. */ void vw_DeleteOpenGLContext() { if (!GLContext) return; SDL_GL_DeleteContext(GLContext); GLContext = nullptr; } /* * Initialize (or reinitialize) and setup OpenGL related stuff. */ void vw_InitOpenGLStuff(int Width, int Height, int *MSAA, int *CSAA) { glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glPolygonMode(GL_FRONT, GL_FILL); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glClearDepth(1.0); glClearStencil(0); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); if (DevCaps.OpenGL_3_0_supported) { MainFBO = vw_BuildFBO(Width, Height, true, true, *MSAA, CSAA); ResolveFBO = vw_BuildFBO(Width, Height, true, false); if (!MainFBO || !ResolveFBO) { *MSAA = 0; MainFBO.reset(); ResolveFBO.reset(); DevCaps.FramebufferObjectDepthSize = 0; } } else { *MSAA = 0; MainFBO.reset(); ResolveFBO.reset(); DevCaps.FramebufferObjectDepthSize = 0; } } /* * Release OpenGL related stuff. */ void vw_ReleaseOpenGLStuff() { vw_ReleaseAllShaders(); MainFBO.reset(); ResolveFBO.reset(); } /* * Get device capability. */ const sDevCaps &vw_DevCaps() { return DevCaps; } /* * Internal access to DevCaps, with write access. */ sDevCaps &ChangeDevCaps() { return DevCaps; } /* * Resize scene. */ void vw_ResizeScene(float FieldOfViewAngle, float AspectRatio, float zNearClip, float zFarClip) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(FieldOfViewAngle, AspectRatio, zNearClip, zFarClip); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /* * Clear buffers. */ void vw_Clear(int mask) { GLbitfield glmask{0}; if (mask & 0x1000) glmask = glmask | GL_COLOR_BUFFER_BIT; if (mask & 0x0100) glmask = glmask | GL_DEPTH_BUFFER_BIT; if (mask & 0x0010) glmask = glmask | GL_ACCUM_BUFFER_BIT; if (mask & 0x0001) glmask = glmask | GL_STENCIL_BUFFER_BIT; glClear(glmask); } /* * Begin rendering. */ void vw_BeginRendering(int mask) { vw_BindFBO(MainFBO); vw_Clear(mask); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /* * End rendering. */ void vw_EndRendering() { if (MainFBO) { std::shared_ptr tmpEmptyFBO{}; if (MainFBO->ColorTexture) vw_DrawColorFBO(MainFBO, tmpEmptyFBO); else { // if we use multisamples, should blit to color buffer first vw_BlitFBO(MainFBO, ResolveFBO); vw_DrawColorFBO(ResolveFBO, tmpEmptyFBO); } } assert(SDLWindow); SDL_GL_SwapWindow(SDLWindow); } /* * Set virtual internal resolution size and status. */ void vw_SetInternalResolution(float Width, float Height, bool Status) { InternalResolution = Status; if (Status) { InternalWidth = Width; InternalHeight = Height; } } /* * Get virtual internal resolution. */ bool vw_GetInternalResolution(float *Width, float *Height) { if (Width) *Width = InternalWidth; if (Height) *Height = InternalHeight; return InternalResolution; } /* * Set depth range. */ void vw_DepthRange(GLdouble zNear, GLdouble zFar) { glDepthRange(zNear, zFar); } /* * Set viewport data. */ void vw_SetViewport(GLint x, GLint y, GLsizei width, GLsizei height, eOrigin Origin) { assert(SDLWindow); if (Origin == eOrigin::upper_left) { int SDLWindowWidth, SDLWindowHeight; SDL_GetWindowSize(SDLWindow, &SDLWindowWidth, &SDLWindowHeight); y = SDLWindowHeight - y - height; } glViewport(x, y, width, height); } /* * Get viewport data. */ void vw_GetViewport(float *x, float *y, float *width, float *height) { GLfloat buff[4]; glGetFloatv(GL_VIEWPORT, buff); if (x) *x = buff[0]; if (y) *y = buff[1]; if (width) *width = buff[2]; if (height) *height = buff[3]; } /* * Get viewport data. */ void vw_GetViewport(int *x, int *y, int *width, int *height) { GLint buff[4]; glGetIntegerv(GL_VIEWPORT, buff); if (x) *x = buff[0]; if (y) *y = buff[1]; if (width) *width = buff[2]; if (height) *height = buff[3]; } /* * Set what facets can be culled. */ void vw_CullFace(eCullFace mode) { if (mode == eCullFace::NONE) { glDisable(GL_CULL_FACE); return; } glEnable(GL_CULL_FACE); glCullFace(static_cast(mode)); } /* * Set the scale and units used to calculate depth values. */ void vw_PolygonOffset(bool status, GLfloat factor, GLfloat units) { if (status) glEnable(GL_POLYGON_OFFSET_FILL); else glDisable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } /* * Specify clear values for the color buffers. */ void vw_SetClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { glClearColor(red, green, blue, alpha); } /* * Set color. */ void vw_SetColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { glColor4f(red, green, blue, alpha); } /* * Specifies whether the individual color components in the frame buffer can or cannot be written. */ void vw_SetColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { glColorMask(red, green, blue, alpha); } /* * Set depth buffer. */ void vw_DepthTest(bool mode, eCompareFunc func) { if (mode) { glEnable(GL_DEPTH_TEST); glDepthFunc(static_cast(func)); } else glDisable(GL_DEPTH_TEST); } } // viewizard namespace