CSCI441 OpenGL Library 6.1.3.0
CS@Mines CSCI441 Computer Graphics Course Library
Loading...
Searching...
No Matches
OpenGLEngine.hpp
Go to the documentation of this file.
1
12#ifndef CSCI441_OPENGL_ENGINE_HPP
13#define CSCI441_OPENGL_ENGINE_HPP
14
15#include "constants.h"
16#include "FontUtils.hpp"
17#include "LogUtils.hpp"
18#include "OpenGLUtils.hpp"
19
20#ifdef CSCI441_USE_GLEW
21 #include <GL/glew.h>
22#else
23 #include <glad/gl.h>
24#endif
25
26#include <GLFW/glfw3.h>
27
28#ifdef CSCI441_OPENGL_ENGINE_IMPLEMENTATION
29#define STB_IMAGE_WRITE_IMPLEMENTATION
30#endif
31#include <stb_image_write.h>
32
33#include <chrono>
34#include <cstdio>
35#include <cstring>
36#include <ctime>
37#include <deque>
38#include <set>
39#include <string>
40#include <memory>
41
42namespace CSCI441 {
43
54 public:
58 OpenGLEngine(const OpenGLEngine&) = delete;
63
67 OpenGLEngine(OpenGLEngine&&) noexcept;
72 OpenGLEngine& operator=(OpenGLEngine&&) noexcept;
73
78 virtual ~OpenGLEngine();
79
87 virtual void initialize();
91 virtual void run() = 0;
99 virtual void shutdown();
100
104 [[maybe_unused]] virtual bool saveScreenshot(const char* FILENAME) noexcept final;
105
110 [[maybe_unused]] virtual void turnDebuggingOn() noexcept final { DEBUG = true; }
115 virtual void turnDebuggingOff() noexcept final { DEBUG = false; }
119 [[maybe_unused]] [[nodiscard]] virtual bool isDebuggingEnabled() const noexcept final { return DEBUG; }
120
126 [[maybe_unused]] [[nodiscard]] virtual bool isExtensionEnabled(const std::string EXT) const noexcept final { return _extensions.find(EXT) != _extensions.end(); }
127
136 virtual void setCurrentWindowSize(const int WINDOW_WIDTH, const int WINDOW_HEIGHT) final { mWindowWidth = WINDOW_WIDTH; mWindowHeight = WINDOW_HEIGHT; }
145 virtual void setCurrentFramebufferSize(const int FRAMEBUFFER_WIDTH, const int FRAMEBUFFER_HEIGHT) final { mFramebufferWidth = FRAMEBUFFER_WIDTH; mFramebufferHeight = FRAMEBUFFER_HEIGHT; }
149 [[maybe_unused]] [[nodiscard]] virtual int getWindowHeight() const noexcept final { return mWindowHeight; }
153 [[maybe_unused]] [[nodiscard]] virtual int getWindowWidth() const noexcept final { return mWindowWidth; }
157 [[maybe_unused]] [[nodiscard]] virtual GLFWwindow* getWindow() const noexcept final { return mpWindow; }
158
162 [[maybe_unused]] virtual void setWindowShouldClose() final { glfwSetWindowShouldClose(mpWindow, GLFW_TRUE); }
163
167 [[nodiscard]] virtual unsigned short getError() noexcept final {
168 const unsigned short storedErrorCode = mErrorCode; // store current error code
171 }
172 mErrorCode = OPENGL_ENGINE_ERROR_NO_ERROR; // reset error code
173 return storedErrorCode; // return previously stored error code
174 }
175
179 static constexpr unsigned short OPENGL_ENGINE_ERROR_NO_ERROR = 0;
183 static constexpr unsigned short OPENGL_ENGINE_ERROR_GLFW_INIT = 1;
187 static constexpr unsigned short OPENGL_ENGINE_ERROR_GLFW_WINDOW = 2;
191 static constexpr unsigned short OPENGL_ENGINE_ERROR_GLEW_INIT = 3;
195 static constexpr unsigned short OPENGL_ENGINE_ERROR_GLAD_INIT = 4;
199 static constexpr unsigned short OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT = 5;
203 static constexpr unsigned short OPENGL_ENGINE_ERROR_UNKNOWN = 6;
204 // note to developers: if more error codes are added, need to update LAST accordingly or
205 // update UNKNOWN to the last value and shift
206 // note to developers: if more error codes are added, need to add description to getErrorStringDescription()
211 static constexpr unsigned short OPENGL_ENGINE_ERROR_LAST = OPENGL_ENGINE_ERROR_UNKNOWN;
215 [[maybe_unused]] static constexpr unsigned short OPENGL_ENGINE_ERROR_SIZE = OPENGL_ENGINE_ERROR_LAST + 1;
216
222 static const char* getErrorStringDescription(unsigned short ERROR_CODE) noexcept;
223
224 protected:
237 OpenGLEngine(int OPENGL_MAJOR_VERSION, int OPENGL_MINOR_VERSION, int WINDOW_WIDTH, int WINDOW_HEIGHT, const char* WINDOW_TITLE, bool WINDOW_RESIZABLE = GLFW_FALSE, GLushort NUM_SAMPLES = 0);
238
243 bool DEBUG;
244
248 unsigned int mErrorCode;
249
318 GLFWwindow* mpWindow;
319
326 static void mErrorCallback(const int error, const char* DESCRIPTION) { CSCI441::LogUtils::logError("[ERROR]: %d\n\t%s\n", error, DESCRIPTION ); }
327
331 static void APIENTRY mDebugMessageCallback(const GLenum source, const GLenum type, const GLuint id, const GLenum severity, const GLsizei length, const GLchar* message, const void* userParam) {
332 CSCI441::LogUtils::log("[VERBOSE]: Debug Message (%u): source = %s, type = %s, severity = %s, length = %i, message = %s, userParam = %i\n",
333 id,
334 CSCI441::OpenGLUtils::debugSourceToString(source),
335 CSCI441::OpenGLUtils::debugTypeToString(type),
336 CSCI441::OpenGLUtils::debugSeverityToString(severity),
337 length,
338 message,
339 *(static_cast<const int*>(userParam))
340 );
341 }
342
350 static void mWindowResizeCallback(GLFWwindow* pWindow, int width, int height);
351
359 static void mFramebufferResizeCallback(GLFWwindow* pWindow, int width, int height);
360
380 virtual void mSetupGLFW();
387 virtual void mSetupOpenGL() = 0;
394 virtual void mSetupShaders() {};
400 virtual void mSetupBuffers() {};
406 virtual void mSetupTextures() {}
412 virtual void mSetupFonts() {}
418 virtual void mSetupScene() {};
419
424 virtual void mCleanupScene() {};
429 virtual void mCleanupFonts() {}
434 virtual void mCleanupTextures() {}
439 virtual void mCleanupBuffers() {};
444 virtual void mCleanupShaders() {};
449 virtual void mCleanupOpenGL() {};
456 virtual void mCleanupGLFW();
457
462 virtual void mReloadShaders() final;
463
471 virtual GLdouble clockFrame() final;
472
479 virtual GLdouble fpsAverage() final;
480
481 private:
482 void _setupGLFunctions(); // initialize OpenGL functions
483 void _cleanupGLFunctions() {} // nothing to be done at this time
484
485 void _cleanupSelf(); // delete internal memory
486 void _moveFromSource(OpenGLEngine&);// move members from another instance
487
488 bool _isInitialized; // makes initialization a singleton process
489 bool _isCleanedUp; // makes cleanup a singleton process
490
491 std::set< std::string > _extensions;// set of all available OpenGL extensions
492
493 GLdouble _lastFrameTime;
494 std::deque<GLdouble> _frameTimes;
495 GLint _numFrames;
496 };
497}
498
499inline
501 const int OPENGL_MAJOR_VERSION,
502 const int OPENGL_MINOR_VERSION,
503 const int WINDOW_WIDTH,
504 const int WINDOW_HEIGHT,
505 const char* WINDOW_TITLE,
506 const bool WINDOW_RESIZABLE,
507 const GLushort NUM_SAMPLES
508) : DEBUG(true),
509 mErrorCode(OPENGL_ENGINE_ERROR_NO_ERROR),
510 mOpenGLMajorVersion(OPENGL_MAJOR_VERSION),
511 mOpenGLMinorVersion(OPENGL_MINOR_VERSION),
512 mOpenGLMajorVersionRequested(OPENGL_MAJOR_VERSION),
513 mOpenGLMinorVersionRequested(OPENGL_MINOR_VERSION),
514 mOpenGLMajorVersionCreated(0),
515 mOpenGLMinorVersionCreated(0),
516 mWindowWidth(WINDOW_WIDTH),
517 mWindowHeight(WINDOW_HEIGHT),
518 mFramebufferWidth(WINDOW_WIDTH),
519 mFramebufferHeight(WINDOW_HEIGHT),
520 mWindowResizable(WINDOW_RESIZABLE),
521 mNumSamplesPerFragment(NUM_SAMPLES),
522 mWindowTitle(nullptr),
523 mpWindow(nullptr),
524 _isInitialized(false),
525 _isCleanedUp(false),
526 _lastFrameTime(0),
527 _numFrames(0)
528{
529 mWindowTitle = new char[ strlen(WINDOW_TITLE) + 1 ];
530 strncpy(mWindowTitle, WINDOW_TITLE, strlen(WINDOW_TITLE) + 1);
531 mWindowTitle[strlen(WINDOW_TITLE)] = '\0';
532
533 CSCI441::FontUtils::setWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
534 CSCI441::FontUtils::setFontSize(1.0f / static_cast<GLfloat>(WINDOW_WIDTH), 1.0f / static_cast<GLfloat>(WINDOW_HEIGHT) );
535}
536
537inline
540) noexcept :
541 DEBUG(true),
542 mErrorCode(OPENGL_ENGINE_ERROR_NO_ERROR),
543 mOpenGLMajorVersion(0),
544 mOpenGLMinorVersion(0),
545 mOpenGLMajorVersionRequested(0),
546 mOpenGLMinorVersionRequested(0),
547 mOpenGLMajorVersionCreated(0),
548 mOpenGLMinorVersionCreated(0),
549 mWindowWidth(0),
550 mWindowHeight(0),
551 mFramebufferWidth(0),
552 mFramebufferHeight(0),
553 mWindowResizable(false),
554 mNumSamplesPerFragment(0),
555 mWindowTitle(nullptr),
556 mpWindow(nullptr),
557 _isInitialized(false),
558 _isCleanedUp(false),
559 _lastFrameTime(0),
560 _numFrames(0)
561{
562 _moveFromSource(src);
563}
564
565inline
567 OpenGLEngine&& src
568) noexcept
569{
570 if (this != &src) {
571 _cleanupSelf();
572 _moveFromSource(src);
573 }
574 return *this;
575}
576
577
578inline
580{
581 _cleanupSelf();
582}
583
584inline
585void CSCI441::OpenGLEngine::_cleanupSelf()
586{
587 delete[] mWindowTitle;
588 mWindowTitle = nullptr;
589}
590
591inline
592void CSCI441::OpenGLEngine::_moveFromSource(
593 OpenGLEngine& src
594) {
595 DEBUG = src.DEBUG;
596 src.DEBUG = false;
597
598 mErrorCode = src.mErrorCode;
599 src.mErrorCode = OPENGL_ENGINE_ERROR_NO_ERROR;
600
601 mOpenGLMajorVersion = src.mOpenGLMajorVersion;
602 src.mOpenGLMajorVersion = 0;
603
604 mOpenGLMinorVersion = src.mOpenGLMinorVersion;
605 src.mOpenGLMinorVersion = 0;
606
607 mOpenGLMajorVersionRequested = src.mOpenGLMajorVersionRequested;
608 src.mOpenGLMajorVersionRequested = 0;
609
610 mOpenGLMinorVersionRequested = src.mOpenGLMinorVersionRequested;
611 src.mOpenGLMinorVersionRequested = 0;
612
613 mOpenGLMajorVersionCreated = src.mOpenGLMajorVersionCreated;
614 src.mOpenGLMajorVersionCreated = 0;
615
616 mOpenGLMinorVersionCreated = src.mOpenGLMinorVersionCreated;
617 src.mOpenGLMinorVersionCreated = 0;
618
619 mWindowWidth = src.mWindowWidth;
620 src.mWindowWidth = 0;
621
622 mWindowHeight = src.mWindowHeight;
623 src.mWindowHeight = 0;
624
625 mWindowResizable = src.mWindowResizable;
626 src.mWindowResizable = false;
627
628 mNumSamplesPerFragment = src.mNumSamplesPerFragment;
629 src.mNumSamplesPerFragment = 0;
630
631 mWindowTitle = src.mWindowTitle;
632 src.mWindowTitle = nullptr;
633
634 mpWindow = src.mpWindow;
635 src.mpWindow = nullptr;
636
637 _isInitialized = src._isInitialized;
638 src._isInitialized = false;
639
640 _isCleanedUp = src._isCleanedUp;
641 src._isCleanedUp = false;
642
643 _extensions = std::move(src._extensions);
644}
645
646inline
648{
649 if( !_isInitialized ) {
650 const std::chrono::steady_clock::time_point initBegin = std::chrono::steady_clock::now();
651
652 if (DEBUG) {
653 CSCI441::LogUtils::log("[INFO]: Using CSCI441 Library v%d.%d.%d.%d\n", CSCI441::VERSION_MAJOR, CSCI441::VERSION_MINOR, CSCI441::VERSION_REVISION, CSCI441::VERSION_PATCH);
654 }
655
656 const std::chrono::steady_clock::time_point glfwBegin = std::chrono::steady_clock::now();
657 mSetupGLFW(); // initialize GLFW and set up a window
658 glfwGetFramebufferSize( mpWindow, &mFramebufferWidth, &mFramebufferHeight );
659 const std::chrono::steady_clock::time_point glfwEnd = std::chrono::steady_clock::now();
660
661 const std::chrono::steady_clock::time_point openGLBegin = std::chrono::steady_clock::now();
662 _setupGLFunctions(); // create OpenGL function pointers
663 mSetupOpenGL(); // create the OpenGL context
664
665 // enable/disable multisampling as appropriate
666 if (mNumSamplesPerFragment > 0) {
667 glEnable(GL_MULTISAMPLE);
668 } else {
669 glDisable(GL_MULTISAMPLE);
670 }
671
672 // get OpenGL context information
673 if( DEBUG ) {
674 // if wanting debug information with Version 4.3 or higher
675 if( mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3) ) {
676 // check if debug context was created
677 int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
678 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
679 // register callback to synchronously print any debug messages without having to call glGetError()
680 glEnable(GL_DEBUG_OUTPUT);
681 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
682 glDebugMessageCallback(mDebugMessageCallback, nullptr);
683 glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE );
684 }
685 }
686
687 CSCI441::OpenGLUtils::printOpenGLInfo();
688 }
689 const std::chrono::steady_clock::time_point openGLEnd = std::chrono::steady_clock::now();
690 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupOpenGL()");
691
692 const std::chrono::steady_clock::time_point shadersBegin = std::chrono::steady_clock::now();
693 mSetupShaders(); // transfer, compile, link shaders on GPU
694 const std::chrono::steady_clock::time_point shadersEnd = std::chrono::steady_clock::now();
695 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupShaders()");
696
697 const std::chrono::steady_clock::time_point buffersBegin = std::chrono::steady_clock::now();
698 mSetupBuffers(); // register Buffers on GPU
699 const std::chrono::steady_clock::time_point buffersEnd = std::chrono::steady_clock::now();
700 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupBuffers()");
701
702 const std::chrono::steady_clock::time_point texturesBegin = std::chrono::steady_clock::now();
703 mSetupTextures(); // register Textures on GPU
704 const std::chrono::steady_clock::time_point texturesEnd = std::chrono::steady_clock::now();
705 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupTextures()");
706
707 const std::chrono::steady_clock::time_point fontsBegin = std::chrono::steady_clock::now();
708 mSetupFonts(); // register Fonts on GPU
709 const std::chrono::steady_clock::time_point fontsEnd = std::chrono::steady_clock::now();
710 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupFonts()");
711
712 const std::chrono::steady_clock::time_point sceneBegin = std::chrono::steady_clock::now();
713 mSetupScene(); // setup any scene specific information
714 const std::chrono::steady_clock::time_point sceneEnd = std::chrono::steady_clock::now();
715 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupScene()");
716
717 _isInitialized = true;
718 _isCleanedUp = false;
719
720 const std::chrono::steady_clock::time_point initEnd = std::chrono::steady_clock::now();
721 if (DEBUG) {
722 CSCI441::LogUtils::log("\n[INFO]: Setup complete\n");
723 CSCI441::LogUtils::log("[INFO]: Init Time: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(initEnd - initBegin ).count() ) / 1000000.0);
724 CSCI441::LogUtils::log("[INFO]: \tGLFW: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(glfwEnd - glfwBegin ).count() ) / 1000000.0);
725 CSCI441::LogUtils::log("[INFO]: \tOpenGL: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(openGLEnd - openGLBegin ).count() ) / 1000000.0);
726 CSCI441::LogUtils::log("[INFO]: \tShaders: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shadersEnd - shadersBegin ).count() ) / 1000000.0);
727 CSCI441::LogUtils::log("[INFO]: \tBuffers: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(buffersEnd - buffersBegin ).count() ) / 1000000.0);
728 CSCI441::LogUtils::log("[INFO]: \tTextures: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(texturesEnd - texturesBegin).count() ) / 1000000.0);
729 CSCI441::LogUtils::log("[INFO]: \tFonts: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(fontsEnd - fontsBegin ).count() ) / 1000000.0);
730 CSCI441::LogUtils::log("[INFO]: \tScene: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(sceneEnd - sceneBegin ).count() ) / 1000000.0);
731 }
732 }
733}
734
735inline
737 const unsigned short ERROR_CODE
738) noexcept {
739 switch (ERROR_CODE) {
740 case OPENGL_ENGINE_ERROR_NO_ERROR: return "No error";
741 case OPENGL_ENGINE_ERROR_GLFW_INIT: return "Error initializing GLFW";
742 case OPENGL_ENGINE_ERROR_GLFW_WINDOW: return "Error initializing GLFW window";
743 case OPENGL_ENGINE_ERROR_GLEW_INIT: return "Error initializing GLEW";
744 case OPENGL_ENGINE_ERROR_GLAD_INIT: return "Error initializing GLAD";
745 case OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT: return "Error saving screenshot";
746 default: return "Unknown error code";
747 }
748}
749
750inline
752 const char* FILENAME
753) noexcept {
754 // Generate a name based on current timestamp if not provided
755 const std::string filename =
756 (
757 FILENAME == nullptr
758 ? "Screenshot_" + std::to_string(time(nullptr)) + ".png"
759 : FILENAME
760 );
761
762 // Get size
763 GLint viewport[4];
764 glGetIntegerv(GL_VIEWPORT, viewport);
765 const GLsizei x = viewport[0], y = viewport[1], width = viewport[2], height = viewport[3];
766
767 // Read pixel data
768 constexpr int CHANNELS = 4; // RGBA
769 const auto bytes = new GLubyte[width*height*CHANNELS];
770 glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
771
772 stbi_flip_vertically_on_write(true);
773
774 if( !stbi_write_png(filename.c_str(), width, height, CHANNELS, bytes, width*CHANNELS) ) {
775 CSCI441::LogUtils::logError("[ERROR]: Could not save screenshot\n");
776 mErrorCode = OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT;
777 } else if(DEBUG) {
778 CSCI441::LogUtils::log("[INFO]: Screenshot saved to %s\n", filename.c_str());
779 }
780
781 delete[] bytes;
782
783 return (mErrorCode == OPENGL_ENGINE_ERROR_NO_ERROR);
784}
785
786inline
788{
789 // set what function to use when registering errors
790 // this is the ONLY GLFW function that can be called BEFORE GLFW is initialized
791 // all other GLFW calls must be performed after GLFW has been initialized
792 glfwSetErrorCallback(mErrorCallback);
793
794 // initialize GLFW
795 if( !glfwInit() ) {
796 CSCI441::LogUtils::logError("[ERROR]: Could not initialize GLFW\n" );
797 mErrorCode = OPENGL_ENGINE_ERROR_GLFW_INIT;
798 } else {
799 if(DEBUG) CSCI441::LogUtils::log("[INFO]: GLFW v%d.%d.%d initialized\n", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION );
800
801 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, mOpenGLMajorVersion ); // request OpenGL vX.
802 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, mOpenGLMinorVersion ); // request OpenGL v .X
803 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); // request forward compatible OpenGL context
804 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); // request OpenGL Core Profile context
805 glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE ); // request double buffering
806 glfwWindowHint(GLFW_RESIZABLE, mWindowResizable ); // set if our window should be able to be resized
807 glfwWindowHint(GLFW_SAMPLES, mNumSamplesPerFragment); // request number of samples per fragment
808
809 // if wanting debug information with Version 4.3 or higher
810 if( DEBUG
811 && (mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3)) ) {
812 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); // request a debug context
813 }
814
815 // create a window for a given size, with a given title
816 mpWindow = glfwCreateWindow(mWindowWidth, mWindowHeight, mWindowTitle, nullptr, nullptr );
817 if( !mpWindow ) { // if the window could not be created, NULL is returned
818 CSCI441::LogUtils::logError("[ERROR]: GLFW Window could not be created\n" );
819 glfwTerminate();
820 mErrorCode = OPENGL_ENGINE_ERROR_GLFW_WINDOW;
821 } else {
822 if(DEBUG) CSCI441::LogUtils::log("[INFO]: GLFW Window created\n" );
823 glfwMakeContextCurrent(mpWindow); // make the created window the current window
824 glfwSwapInterval(1); // update our screen after at least 1 screen refresh
825 glfwSetInputMode(mpWindow, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // track state of Caps Lock and Num Lock keys
826 glfwSetWindowUserPointer(mpWindow, (void*)this);
827 glfwSetWindowSizeCallback(mpWindow, mWindowResizeCallback);
828 glfwSetFramebufferSizeCallback(mpWindow, mWindowResizeCallback);
829 }
830 }
831}
832
833inline
834void CSCI441::OpenGLEngine::_setupGLFunctions()
835{
836
837#ifdef CSCI441_USE_GLEW
838 glewExperimental = GL_TRUE;
839 const GLenum glewResult = glewInit(); // initialize GLEW
840
841 // check for an error
842 if( glewResult != GLEW_OK ) {
843 CSCI441::LogUtils::logError("[ERROR]: Error initializing GLEW\n");
844 CSCI441::LogUtils::logError("[ERROR]: %s\n", reinterpret_cast<const char *>(glewGetErrorString(glewResult)) );
845 mErrorCode = OPENGL_ENGINE_ERROR_GLEW_INIT;
846 } else {
847 if(DEBUG) {
848 CSCI441::LogUtils::log("[INFO]: GLEW initialized\n");
849 CSCI441::LogUtils::log("[INFO]: Using GLEW %s\n", reinterpret_cast<const char *>(glewGetString(GLEW_VERSION)));
850 }
851 }
852#else
853 int version = gladLoadGL(glfwGetProcAddress);
854 if(version == 0) {
855 CSCI441::LogUtils::logError("[ERROR]: Failed to initialize GLAD\n" );
856 mErrorCode = OPENGL_ENGINE_ERROR_GLAD_INIT;
857 } else {
858 if(DEBUG) {
859 // Successfully loaded OpenGL
860 CSCI441::LogUtils::log("[INFO]: GLAD initialized v%d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
861 }
862 }
863#endif
864
865 if(mErrorCode == OPENGL_ENGINE_ERROR_NO_ERROR) {
866 glGetIntegerv(GL_MAJOR_VERSION, &mOpenGLMajorVersionCreated);
867 glGetIntegerv(GL_MINOR_VERSION, &mOpenGLMinorVersionCreated);
868 if(DEBUG) {
869 CSCI441::LogUtils::log("[INFO]: OpenGL v%d.%d requested\n", mOpenGLMajorVersionRequested, mOpenGLMinorVersionRequested);
870 CSCI441::LogUtils::log("[INFO]: OpenGL v%d.%d created\n", mOpenGLMajorVersionCreated, mOpenGLMinorVersionCreated);
871 }
872
873 GLint numExtensions = 0;
874 glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
875 for (int i = 0; i < numExtensions; i++) {
876 _extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)) );
877 }
878 }
879}
880
881inline
883{
884 if(DEBUG) CSCI441::LogUtils::log("[INFO]: ...closing window...\n" );
885 glfwDestroyWindow(mpWindow ); // close our window
886 mpWindow = nullptr;
887 if(DEBUG) CSCI441::LogUtils::log("[INFO]: ...closing GLFW.....\n" );
888 glfwTerminate();
889}
890
891inline
893{
894 if( !_isCleanedUp ) {
895 const std::chrono::steady_clock::time_point shutdownBegin = std::chrono::steady_clock::now();
896 if (DEBUG) CSCI441::LogUtils::log("\n[INFO]: Shutting down.......\n");
897
898 const std::chrono::steady_clock::time_point sceneBegin = std::chrono::steady_clock::now();
899 mCleanupScene(); // delete scene info from CPU
900 const std::chrono::steady_clock::time_point sceneEnd = std::chrono::steady_clock::now();
901 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupScene()");
902
903 const std::chrono::steady_clock::time_point fontsBegin = std::chrono::steady_clock::now();
904 mCleanupFonts(); // delete textures from GPU
905 const std::chrono::steady_clock::time_point fontsEnd = std::chrono::steady_clock::now();
906 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupFonts()");
907
908 const std::chrono::steady_clock::time_point texturesBegin = std::chrono::steady_clock::now();
909 mCleanupTextures(); // delete textures from GPU
910 const std::chrono::steady_clock::time_point texturesEnd = std::chrono::steady_clock::now();
911 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupTextures()");
912
913 const std::chrono::steady_clock::time_point buffersBegin = std::chrono::steady_clock::now();
914 mCleanupBuffers(); // delete VAOs/VBOs from GPU
915 const std::chrono::steady_clock::time_point buffersEnd = std::chrono::steady_clock::now();
916 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupBuffers()");
917
918 const std::chrono::steady_clock::time_point shadersBegin = std::chrono::steady_clock::now();
919 mCleanupShaders(); // delete shaders from GPU
920 const std::chrono::steady_clock::time_point shadersEnd = std::chrono::steady_clock::now();
921 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupShaders()");
922
923 const std::chrono::steady_clock::time_point openGLBegin = std::chrono::steady_clock::now();
924 mCleanupOpenGL(); // cleanup anything OpenGL related
925 _cleanupGLFunctions(); // cleanup anything function pointer related
926 const std::chrono::steady_clock::time_point openGLEnd = std::chrono::steady_clock::now();
927 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupOpenGL()");
928
929 const std::chrono::steady_clock::time_point glfwBegin = std::chrono::steady_clock::now();
930 mCleanupGLFW(); // shut down GLFW to clean up our context
931 const std::chrono::steady_clock::time_point glfwEnd = std::chrono::steady_clock::now();
932
933 const std::chrono::steady_clock::time_point shutdownEnd = std::chrono::steady_clock::now();
934 if (DEBUG) {
935 CSCI441::LogUtils::log("[INFO]: ..shut down complete!\n");
936 CSCI441::LogUtils::log("[INFO]: Shutdown: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shutdownEnd - shutdownBegin).count() ) / 1000000.0);
937 CSCI441::LogUtils::log("[INFO]: \tScene: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(sceneEnd - sceneBegin ).count() ) / 1000000.0);
938 CSCI441::LogUtils::log("[INFO]: \tFonts: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(fontsEnd - fontsBegin ).count() ) / 1000000.0);
939 CSCI441::LogUtils::log("[INFO]: \tTextures: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(texturesEnd - texturesBegin).count() ) / 1000000.0);
940 CSCI441::LogUtils::log("[INFO]: \tBuffers: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(buffersEnd - buffersBegin ).count() ) / 1000000.0);
941 CSCI441::LogUtils::log("[INFO]: \tShaders: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shadersEnd - shadersBegin ).count() ) / 1000000.0);
942 CSCI441::LogUtils::log("[INFO]: \tOpenGL: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(openGLEnd - openGLBegin ).count() ) / 1000000.0);
943 CSCI441::LogUtils::log("[INFO]: \tGLFW: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(glfwEnd - glfwBegin ).count() ) / 1000000.0);
944 }
945 _isCleanedUp = true;
946 _isInitialized = false;
947 }
948}
949
950inline
952 GLFWwindow* pWindow,
953 const int width,
954 const int height
955) {
956 const auto pEngine = static_cast<OpenGLEngine *>(glfwGetWindowUserPointer(pWindow));
957 pEngine->setCurrentWindowSize(width, height);
959 CSCI441::FontUtils::setFontSize(1.0f / static_cast<GLfloat>(width), 1.0f / static_cast<GLfloat>(height) );
960}
961
962inline
964 GLFWwindow* pWindow,
965 const int width,
966 const int height
967) {
968 const auto pEngine = static_cast<OpenGLEngine *>(glfwGetWindowUserPointer(pWindow));
969 pEngine->setCurrentFramebufferSize(width, height);
970}
971
972inline
974{
975 if (DEBUG) CSCI441::LogUtils::log("\n[INFO]: Removing old shaders...\n");
976 mCleanupShaders();
977 if (DEBUG) CSCI441::LogUtils::log("\n[INFO]: Reloading shaders...\n");
978 mSetupShaders();
979 if (DEBUG) CSCI441::LogUtils::log("\n[INFO]: Shaders reloaded\n");
980}
981
982inline
984 const GLdouble currentTime = glfwGetTime();
985 ++_numFrames;
986 GLdouble fps = ( !_frameTimes.empty() ? _frameTimes.back() : 0.0 );
987 if (currentTime - _lastFrameTime > 0.1667) {
988 fps = static_cast<GLdouble>(_numFrames)/(currentTime - _lastFrameTime);
989 _numFrames = 0;
990 _lastFrameTime = currentTime;
991
992 if (_frameTimes.size() >= 10) _frameTimes.pop_front();
993 _frameTimes.push_back( fps );
994 }
995 return fps;
996}
997
998inline
1000 GLdouble totalFPS = 0;
1001 for( const auto& fpsUnit : _frameTimes ) {
1002 totalFPS += fpsUnit;
1003 }
1004 return totalFPS / static_cast<GLdouble>(_frameTimes.size());
1005}
1006
1007#endif //CSCI441_OPENGL_ENGINE_HPP
Helper functions to work with OpenGL 3.0+.
Abstract Class to run an OpenGL application. The following methods must be overridden:
Definition: OpenGLEngine.hpp:53
virtual void mSetupOpenGL()=0
override to enable specific OpenGL features
virtual bool isExtensionEnabled(const std::string EXT) const noexcept final
Returns if OpenGL extension exists.
Definition: OpenGLEngine.hpp:126
bool DEBUG
if information should be printed to console while running
Definition: OpenGLEngine.hpp:243
OpenGLEngine & operator=(const OpenGLEngine &)=delete
do not allow engines to be copied
int mFramebufferWidth
the framebuffer height of the requested GLFW window
Definition: OpenGLEngine.hpp:295
int mOpenGLMinorVersion
the minor version of the requested OpenGL context
Definition: OpenGLEngine.hpp:259
int mOpenGLMajorVersion
the major version of the requested OpenGL context
Definition: OpenGLEngine.hpp:254
static constexpr unsigned short OPENGL_ENGINE_ERROR_NO_ERROR
no error is present, everything is currently working
Definition: OpenGLEngine.hpp:179
virtual bool saveScreenshot(const char *FILENAME) noexcept final
Save a PNG screenshot of the viewport.
Definition: OpenGLEngine.hpp:751
virtual void setCurrentWindowSize(const int WINDOW_WIDTH, const int WINDOW_HEIGHT) final
Stores the new window size.
Definition: OpenGLEngine.hpp:136
virtual void initialize()
Initialize everything needed for OpenGL Rendering. This includes in order: GLFW, function pointers,...
Definition: OpenGLEngine.hpp:647
virtual void shutdown()
Cleanup everything needed for OpenGL Rendering. This includes freeing memory for data used in: any Sc...
Definition: OpenGLEngine.hpp:892
virtual unsigned short getError() noexcept final
Return current value of error code and clear the error code back to no error.
Definition: OpenGLEngine.hpp:167
virtual void mSetupTextures()
override to register any textures with the GPU
Definition: OpenGLEngine.hpp:406
virtual int getWindowHeight() const noexcept final
Return the height of the window.
Definition: OpenGLEngine.hpp:149
virtual bool isDebuggingEnabled() const noexcept final
Returns if logging is enabled.
Definition: OpenGLEngine.hpp:119
static void mWindowResizeCallback(GLFWwindow *pWindow, int width, int height)
Definition: OpenGLEngine.hpp:951
static constexpr unsigned short OPENGL_ENGINE_ERROR_GLAD_INIT
an error occurred while initializing GLAD
Definition: OpenGLEngine.hpp:195
virtual void mCleanupBuffers()
override to cleanup any buffer objects from the GPU
Definition: OpenGLEngine.hpp:439
virtual void mCleanupScene()
override to cleanup any scene specific information
Definition: OpenGLEngine.hpp:424
virtual void mSetupFonts()
override to register any fonts with the GPU
Definition: OpenGLEngine.hpp:412
virtual void setCurrentFramebufferSize(const int FRAMEBUFFER_WIDTH, const int FRAMEBUFFER_HEIGHT) final
Stores the framebuffer size.
Definition: OpenGLEngine.hpp:145
virtual void mCleanupShaders()
override to cleanup any shaders from the GPU
Definition: OpenGLEngine.hpp:444
virtual void mCleanupTextures()
override to cleanup any textures from the GPU
Definition: OpenGLEngine.hpp:434
virtual GLdouble clockFrame() final
measures the amount of time elapsed since the last time this method was called
Definition: OpenGLEngine.hpp:983
bool mWindowResizable
if the GLFW window can be resized while open
Definition: OpenGLEngine.hpp:304
int mOpenGLMinorVersionRequested
the minor version of the requested OpenGL context
Definition: OpenGLEngine.hpp:271
GLushort mNumSamplesPerFragment
how many samples to fire per fragment
Definition: OpenGLEngine.hpp:310
OpenGLEngine(const OpenGLEngine &)=delete
do not allow engines to be copied
static constexpr unsigned short OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT
an error occurred while taking a screenshot
Definition: OpenGLEngine.hpp:199
int mFramebufferHeight
the framebuffer width of the requested GLFW window
Definition: OpenGLEngine.hpp:299
virtual void turnDebuggingOn() noexcept final
Enable logging to command line.
Definition: OpenGLEngine.hpp:110
static constexpr unsigned short OPENGL_ENGINE_ERROR_GLEW_INIT
an error occurred while initializing GLEW
Definition: OpenGLEngine.hpp:191
static constexpr unsigned short OPENGL_ENGINE_ERROR_GLFW_WINDOW
an error occurred while creating the GLFW window
Definition: OpenGLEngine.hpp:187
virtual void mCleanupFonts()
override to cleanup any fonts from the GPU
Definition: OpenGLEngine.hpp:429
int mOpenGLMajorVersionRequested
the major version of the requested OpenGL context
Definition: OpenGLEngine.hpp:265
static void mErrorCallback(const int error, const char *DESCRIPTION)
We will register this function as GLFW's error callback. When an error within OpenGL occurs,...
Definition: OpenGLEngine.hpp:326
static constexpr unsigned short OPENGL_ENGINE_ERROR_LAST
stores the error code number of the last possible error, this corresponds to the max error code value...
Definition: OpenGLEngine.hpp:211
unsigned int mErrorCode
tracks the current status of the OpenGL engine via error codes
Definition: OpenGLEngine.hpp:248
virtual void run()=0
Initiate the draw loop.
static void APIENTRY mDebugMessageCallback(const GLenum source, const GLenum type, const GLuint id, const GLenum severity, const GLsizei length, const GLchar *message, const void *userParam)
callback called whenever a debug message is signaled
Definition: OpenGLEngine.hpp:331
static void mFramebufferResizeCallback(GLFWwindow *pWindow, int width, int height)
Definition: OpenGLEngine.hpp:963
char * mWindowTitle
the title of the GLFW window
Definition: OpenGLEngine.hpp:314
int mWindowHeight
the window height of the requested GLFW window
Definition: OpenGLEngine.hpp:291
virtual GLdouble fpsAverage() final
Definition: OpenGLEngine.hpp:999
virtual void setWindowShouldClose() final
Tell our engine's window to close.
Definition: OpenGLEngine.hpp:162
int mOpenGLMajorVersionCreated
the major version of the created OpenGL context
Definition: OpenGLEngine.hpp:276
static constexpr unsigned short OPENGL_ENGINE_ERROR_SIZE
stores the number of unique error codes that can be generated
Definition: OpenGLEngine.hpp:215
virtual void mReloadShaders() final
calls mCleanupShaders() followed by mSetupShaders() to reload shader source code from file
Definition: OpenGLEngine.hpp:973
virtual void mSetupShaders()
override to register any shaders with the GPU
Definition: OpenGLEngine.hpp:394
GLFWwindow * mpWindow
pointer to the GLFW window object
Definition: OpenGLEngine.hpp:318
virtual void turnDebuggingOff() noexcept final
Disable logging to command line.
Definition: OpenGLEngine.hpp:115
static const char * getErrorStringDescription(unsigned short ERROR_CODE) noexcept
Returns a string describing what the error code corresponds to.
Definition: OpenGLEngine.hpp:736
virtual int getWindowWidth() const noexcept final
Return the width of the window.
Definition: OpenGLEngine.hpp:153
virtual void mSetupBuffers()
override to register any buffer objects with the GPU
Definition: OpenGLEngine.hpp:400
virtual ~OpenGLEngine()
cleans up our OpenGL Engine by destroying the OpenGL context, GLFW window, and cleaning up all GPU re...
Definition: OpenGLEngine.hpp:579
virtual void mCleanupGLFW()
Destroys the associated GLFW window and terminates the GLFW instance.
Definition: OpenGLEngine.hpp:882
int mOpenGLMinorVersionCreated
the minor version of the created OpenGL context
Definition: OpenGLEngine.hpp:281
int mWindowWidth
the window width of the requested GLFW window
Definition: OpenGLEngine.hpp:286
virtual void mCleanupOpenGL()
override to cleanup any specific OpenGL features
Definition: OpenGLEngine.hpp:449
static constexpr unsigned short OPENGL_ENGINE_ERROR_GLFW_INIT
an error occurred while initializing GLFW
Definition: OpenGLEngine.hpp:183
virtual void mSetupGLFW()
Used to setup everything GLFW related. This includes the OpenGL context and our window....
Definition: OpenGLEngine.hpp:787
virtual void mSetupScene()
override to setup any scene specific information
Definition: OpenGLEngine.hpp:418
static constexpr unsigned short OPENGL_ENGINE_ERROR_UNKNOWN
a new error that does not correspond to a predefined scenario has occurred
Definition: OpenGLEngine.hpp:203
virtual GLFWwindow * getWindow() const noexcept final
Return the window object.
Definition: OpenGLEngine.hpp:157
void setFontSize(GLfloat scaleX, GLfloat scaleY)
set the amount to scale font when drawing
Definition: FontUtils.hpp:180
void setWindowSize(GLint width, GLint height)
store the size of the window
Definition: FontUtils.hpp:172
void log(const char *MSG,...)
log a message to both the standard output stream and file
Definition: LogUtils.hpp:116
void logError(const char *MSG,...)
log a message to both the standard error stream and file
Definition: LogUtils.hpp:128
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17