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