CSCI441 OpenGL Library 6.0.1.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:
235 OpenGLEngine(int OPENGL_MAJOR_VERSION, int OPENGL_MINOR_VERSION, int WINDOW_WIDTH, int WINDOW_HEIGHT, const char* WINDOW_TITLE, bool WINDOW_RESIZABLE = GLFW_FALSE);
236
241 bool DEBUG;
242
246 unsigned int mErrorCode;
247
310 GLFWwindow* mpWindow;
311
318 static void mErrorCallback(const int error, const char* DESCRIPTION) { fprintf(stderr, "[ERROR]: %d\n\t%s\n", error, DESCRIPTION ); }
319
323 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) {
324 fprintf( stdout, "[VERBOSE]: Debug Message (%d): source = %s, type = %s, severity = %s, message = %s\n",
325 id,
326 CSCI441::OpenGLUtils::debugSourceToString(source),
327 CSCI441::OpenGLUtils::debugTypeToString(type),
328 CSCI441::OpenGLUtils::debugSeverityToString(severity),
329 message
330 );
331 }
332
340 static void mWindowResizeCallback(GLFWwindow* pWindow, int width, int height);
341
361 virtual void mSetupGLFW();
368 virtual void mSetupOpenGL() = 0;
375 virtual void mSetupShaders() {};
381 virtual void mSetupBuffers() {};
387 virtual void mSetupTextures() {}
393 virtual void mSetupFonts() {}
399 virtual void mSetupScene() {};
400
405 virtual void mCleanupScene() {};
410 virtual void mCleanupFonts() {}
415 virtual void mCleanupTextures() {}
420 virtual void mCleanupBuffers() {};
425 virtual void mCleanupShaders() {};
430 virtual void mCleanupOpenGL() {};
437 virtual void mCleanupGLFW();
438
443 virtual void mReloadShaders() final;
444
452 virtual GLdouble clockFrame() final;
453
460 virtual GLdouble fpsAverage() final;
461
462 private:
463 void _setupGLFunctions(); // initialize OpenGL functions
464 void _cleanupGLFunctions() {} // nothing to be done at this time
465
466 void _cleanupSelf(); // delete internal memory
467 void _moveFromSource(OpenGLEngine&);// move members from another instance
468
469 bool _isInitialized; // makes initialization a singleton process
470 bool _isCleanedUp; // makes cleanup a singleton process
471
472 std::set< std::string > _extensions;// set of all available OpenGL extensions
473
474 GLdouble _lastFrameTime;
475 std::deque<GLdouble> _frameTimes;
476 GLint _numFrames;
477 };
478}
479
480inline
482 const int OPENGL_MAJOR_VERSION,
483 const int OPENGL_MINOR_VERSION,
484 const int WINDOW_WIDTH,
485 const int WINDOW_HEIGHT,
486 const char* WINDOW_TITLE,
487 const bool WINDOW_RESIZABLE
488) : DEBUG(true),
489 mErrorCode(OPENGL_ENGINE_ERROR_NO_ERROR),
490 mOpenGLMajorVersion(OPENGL_MAJOR_VERSION),
491 mOpenGLMinorVersion(OPENGL_MINOR_VERSION),
492 mOpenGLMajorVersionRequested(OPENGL_MAJOR_VERSION),
493 mOpenGLMinorVersionRequested(OPENGL_MINOR_VERSION),
494 mOpenGLMajorVersionCreated(0),
495 mOpenGLMinorVersionCreated(0),
496 mWindowWidth(WINDOW_WIDTH),
497 mWindowHeight(WINDOW_HEIGHT),
498 mFramebufferWidth(WINDOW_WIDTH),
499 mFramebufferHeight(WINDOW_HEIGHT),
500 mWindowResizable(WINDOW_RESIZABLE),
501 mWindowTitle(nullptr),
502 mpWindow(nullptr),
503 _isInitialized(false),
504 _isCleanedUp(false),
505 _lastFrameTime(0),
506 _numFrames(0)
507{
508 mWindowTitle = new char[ strlen(WINDOW_TITLE) + 1 ];
509 strncpy(mWindowTitle, WINDOW_TITLE, strlen(WINDOW_TITLE) + 1);
510 mWindowTitle[strlen(WINDOW_TITLE)] = '\0';
511
512 CSCI441::FontUtils::setWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
513 CSCI441::FontUtils::setFontSize(1.0f / static_cast<GLfloat>(WINDOW_WIDTH), 1.0f / static_cast<GLfloat>(WINDOW_HEIGHT) );
514}
515
516inline
519) noexcept :
520 DEBUG(true),
521 mErrorCode(OPENGL_ENGINE_ERROR_NO_ERROR),
522 mOpenGLMajorVersion(0),
523 mOpenGLMinorVersion(0),
524 mOpenGLMajorVersionRequested(0),
525 mOpenGLMinorVersionRequested(0),
526 mOpenGLMajorVersionCreated(0),
527 mOpenGLMinorVersionCreated(0),
528 mWindowWidth(0),
529 mWindowHeight(0),
530 mFramebufferWidth(0),
531 mFramebufferHeight(0),
532 mWindowResizable(false),
533 mWindowTitle(nullptr),
534 mpWindow(nullptr),
535 _isInitialized(false),
536 _isCleanedUp(false),
537 _lastFrameTime(0),
538 _numFrames(0)
539{
540 _moveFromSource(src);
541}
542
543inline
545 OpenGLEngine&& src
546) noexcept
547{
548 if (this != &src) {
549 _cleanupSelf();
550 _moveFromSource(src);
551 }
552 return *this;
553}
554
555
556inline
558{
559 _cleanupSelf();
560}
561
562inline
563void CSCI441::OpenGLEngine::_cleanupSelf()
564{
565 delete[] mWindowTitle;
566 mWindowTitle = nullptr;
567}
568
569inline
570void CSCI441::OpenGLEngine::_moveFromSource(
571 OpenGLEngine& src
572) {
573 DEBUG = src.DEBUG;
574 src.DEBUG = false;
575
576 mErrorCode = src.mErrorCode;
577 src.mErrorCode = OPENGL_ENGINE_ERROR_NO_ERROR;
578
579 mOpenGLMajorVersion = src.mOpenGLMajorVersion;
580 src.mOpenGLMajorVersion = 0;
581
582 mOpenGLMinorVersion = src.mOpenGLMinorVersion;
583 src.mOpenGLMinorVersion = 0;
584
585 mOpenGLMajorVersionRequested = src.mOpenGLMajorVersionRequested;
586 src.mOpenGLMajorVersionRequested = 0;
587
588 mOpenGLMinorVersionRequested = src.mOpenGLMinorVersionRequested;
589 src.mOpenGLMinorVersionRequested = 0;
590
591 mOpenGLMajorVersionCreated = src.mOpenGLMajorVersionCreated;
592 src.mOpenGLMajorVersionCreated = 0;
593
594 mOpenGLMinorVersionCreated = src.mOpenGLMinorVersionCreated;
595 src.mOpenGLMinorVersionCreated = 0;
596
597 mWindowWidth = src.mWindowWidth;
598 src.mWindowWidth = 0;
599
600 mWindowHeight = src.mWindowHeight;
601 src.mWindowHeight = 0;
602
603 mWindowResizable = src.mWindowResizable;
604 src.mWindowResizable = false;
605
606 mWindowTitle = src.mWindowTitle;
607 src.mWindowTitle = nullptr;
608
609 mpWindow = src.mpWindow;
610 src.mpWindow = nullptr;
611
612 _isInitialized = src._isInitialized;
613 src._isInitialized = false;
614
615 _isCleanedUp = src._isCleanedUp;
616 src._isCleanedUp = false;
617
618 _extensions = std::move(src._extensions);
619}
620
621inline
623{
624 if( !_isInitialized ) {
625 const std::chrono::steady_clock::time_point initBegin = std::chrono::steady_clock::now();
626
627 if (DEBUG) {
628 fprintf(stdout, "[INFO]: Using CSCI441 Library v%d.%d.%d.%d\n", CSCI441::VERSION_MAJOR, CSCI441::VERSION_MINOR, CSCI441::VERSION_REVISION, CSCI441::VERSION_PATCH);
629 }
630
631 const std::chrono::steady_clock::time_point glfwBegin = std::chrono::steady_clock::now();
632 mSetupGLFW(); // initialize GLFW and set up a window
633 glfwGetFramebufferSize( mpWindow, &mFramebufferWidth, &mFramebufferHeight );
634 const std::chrono::steady_clock::time_point glfwEnd = std::chrono::steady_clock::now();
635
636 const std::chrono::steady_clock::time_point openGLBegin = std::chrono::steady_clock::now();
637 _setupGLFunctions(); // create OpenGL function pointers
638 mSetupOpenGL(); // create the OpenGL context
639
640 // get OpenGL context information
641 if( DEBUG ) {
642 // if wanting debug information with Version 4.3 or higher
643 if( mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3) ) {
644 // check if debug context was created
645 int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
646 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
647 // register callback to synchronously print any debug messages without having to call glGetError()
648 glEnable(GL_DEBUG_OUTPUT);
649 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
650 glDebugMessageCallback(mDebugMessageCallback, nullptr);
651 glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE );
652 }
653 }
654
655 CSCI441::OpenGLUtils::printOpenGLInfo();
656 }
657 const std::chrono::steady_clock::time_point openGLEnd = std::chrono::steady_clock::now();
658 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupOpenGL()");
659
660 const std::chrono::steady_clock::time_point shadersBegin = std::chrono::steady_clock::now();
661 mSetupShaders(); // transfer, compile, link shaders on GPU
662 const std::chrono::steady_clock::time_point shadersEnd = std::chrono::steady_clock::now();
663 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupShaders()");
664
665 const std::chrono::steady_clock::time_point buffersBegin = std::chrono::steady_clock::now();
666 mSetupBuffers(); // register Buffers on GPU
667 const std::chrono::steady_clock::time_point buffersEnd = std::chrono::steady_clock::now();
668 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupBuffers()");
669
670 const std::chrono::steady_clock::time_point texturesBegin = std::chrono::steady_clock::now();
671 mSetupTextures(); // register Textures on GPU
672 const std::chrono::steady_clock::time_point texturesEnd = std::chrono::steady_clock::now();
673 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupTextures()");
674
675 const std::chrono::steady_clock::time_point fontsBegin = std::chrono::steady_clock::now();
676 mSetupFonts(); // register Fonts on GPU
677 const std::chrono::steady_clock::time_point fontsEnd = std::chrono::steady_clock::now();
678 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupFonts()");
679
680 const std::chrono::steady_clock::time_point sceneBegin = std::chrono::steady_clock::now();
681 mSetupScene(); // setup any scene specific information
682 const std::chrono::steady_clock::time_point sceneEnd = std::chrono::steady_clock::now();
683 CSCI441::OpenGLUtils::checkOpenGLErrors("post mSetupScene()");
684
685 _isInitialized = true;
686 _isCleanedUp = false;
687
688 const std::chrono::steady_clock::time_point initEnd = std::chrono::steady_clock::now();
689 if (DEBUG) {
690 fprintf(stdout, "\n[INFO]: Setup complete\n");
691 fprintf(stdout, "[INFO]: Init Time: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(initEnd - initBegin ).count() ) / 1000000.0);
692 fprintf(stdout, "[INFO]: \tGLFW: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(glfwEnd - glfwBegin ).count() ) / 1000000.0);
693 fprintf(stdout, "[INFO]: \tOpenGL: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(openGLEnd - openGLBegin ).count() ) / 1000000.0);
694 fprintf(stdout, "[INFO]: \tShaders: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shadersEnd - shadersBegin ).count() ) / 1000000.0);
695 fprintf(stdout, "[INFO]: \tBuffers: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(buffersEnd - buffersBegin ).count() ) / 1000000.0);
696 fprintf(stdout, "[INFO]: \tTextures: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(texturesEnd - texturesBegin).count() ) / 1000000.0);
697 fprintf(stdout, "[INFO]: \tFonts: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(fontsEnd - fontsBegin ).count() ) / 1000000.0);
698 fprintf(stdout, "[INFO]: \tScene: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(sceneEnd - sceneBegin ).count() ) / 1000000.0);
699
700 }
701 }
702}
703
704inline
706 const unsigned short ERROR_CODE
707) noexcept {
708 switch (ERROR_CODE) {
709 case OPENGL_ENGINE_ERROR_NO_ERROR: return "No error";
710 case OPENGL_ENGINE_ERROR_GLFW_INIT: return "Error initializing GLFW";
711 case OPENGL_ENGINE_ERROR_GLFW_WINDOW: return "Error initializing GLFW window";
712 case OPENGL_ENGINE_ERROR_GLEW_INIT: return "Error initializing GLEW";
713 case OPENGL_ENGINE_ERROR_GLAD_INIT: return "Error initializing GLAD";
714 case OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT: return "Error saving screenshot";
715 default: return "Unknown error code";
716 }
717}
718
719inline
721 const char* FILENAME
722) noexcept {
723 // Generate a name based on current timestamp if not provided
724 const std::string filename =
725 (
726 FILENAME == nullptr
727 ? "Screenshot_" + std::to_string(time(nullptr)) + ".png"
728 : FILENAME
729 );
730
731 // Get size
732 GLint viewport[4];
733 glGetIntegerv(GL_VIEWPORT, viewport);
734 const GLsizei x = viewport[0], y = viewport[1], width = viewport[2], height = viewport[3];
735
736 // Read pixel data
737 constexpr int CHANNELS = 4; // RGBA
738 const auto bytes = new GLubyte[width*height*CHANNELS];
739 glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
740
741 stbi_flip_vertically_on_write(true);
742
743 if( !stbi_write_png(filename.c_str(), width, height, CHANNELS, bytes, width*CHANNELS) ) {
744 fprintf(stderr, "[ERROR]: Could not save screenshot\n");
745 mErrorCode = OPENGL_ENGINE_ERROR_TAKE_SCREENSHOT;
746 } else if(DEBUG) {
747 fprintf(stdout, "[INFO]: Screenshot saved to %s\n", filename.c_str());
748 }
749
750 delete[] bytes;
751
752 return (mErrorCode == OPENGL_ENGINE_ERROR_NO_ERROR);
753}
754
755inline
757{
758 // set what function to use when registering errors
759 // this is the ONLY GLFW function that can be called BEFORE GLFW is initialized
760 // all other GLFW calls must be performed after GLFW has been initialized
761 glfwSetErrorCallback(mErrorCallback);
762
763 // initialize GLFW
764 if( !glfwInit() ) {
765 fprintf( stderr, "[ERROR]: Could not initialize GLFW\n" );
766 mErrorCode = OPENGL_ENGINE_ERROR_GLFW_INIT;
767 } else {
768 if(DEBUG) fprintf( stdout, "[INFO]: GLFW v%d.%d.%d initialized\n", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION );
769
770 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, mOpenGLMajorVersion ); // request OpenGL vX.
771 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, mOpenGLMinorVersion ); // request OpenGL v .X
772 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); // request forward compatible OpenGL context
773 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); // request OpenGL Core Profile context
774 glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE ); // request double buffering
775 glfwWindowHint(GLFW_RESIZABLE, mWindowResizable ); // set if our window should be able to be resized
776
777 // if wanting debug information with Version 4.3 or higher
778 if( DEBUG
779 && (mOpenGLMajorVersion > 4 || (mOpenGLMajorVersion == 4 && mOpenGLMinorVersion >= 3)) ) {
780 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); // request a debug context
781 }
782
783 // create a window for a given size, with a given title
784 mpWindow = glfwCreateWindow(mWindowWidth, mWindowHeight, mWindowTitle, nullptr, nullptr );
785 if( !mpWindow ) { // if the window could not be created, NULL is returned
786 fprintf( stderr, "[ERROR]: GLFW Window could not be created\n" );
787 glfwTerminate();
788 mErrorCode = OPENGL_ENGINE_ERROR_GLFW_WINDOW;
789 } else {
790 if(DEBUG) fprintf( stdout, "[INFO]: GLFW Window created\n" );
791 glfwMakeContextCurrent(mpWindow); // make the created window the current window
792 glfwSwapInterval(1); // update our screen after at least 1 screen refresh
793 glfwSetInputMode(mpWindow, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // track state of Caps Lock and Num Lock keys
794 glfwSetWindowUserPointer(mpWindow, (void*)this);
795 glfwSetWindowSizeCallback(mpWindow, mWindowResizeCallback);
796 }
797 }
798}
799
800inline
801void CSCI441::OpenGLEngine::_setupGLFunctions()
802{
803
804#ifdef CSCI441_USE_GLEW
805 glewExperimental = GL_TRUE;
806 const GLenum glewResult = glewInit(); // initialize GLEW
807
808 // check for an error
809 if( glewResult != GLEW_OK ) {
810 fprintf( stderr, "[ERROR]: Error initializing GLEW\n");
811 fprintf( stderr, "[ERROR]: %s\n", reinterpret_cast<const char *>(glewGetErrorString(glewResult)) );
812 mErrorCode = OPENGL_ENGINE_ERROR_GLEW_INIT;
813 } else {
814 if(DEBUG) {
815 fprintf(stdout, "[INFO]: GLEW initialized\n");
816 fprintf(stdout, "[INFO]: Using GLEW %s\n", reinterpret_cast<const char *>(glewGetString(GLEW_VERSION)));
817 }
818 }
819#else
820 int version = gladLoadGL(glfwGetProcAddress);
821 if(version == 0) {
822 fprintf(stderr, "[ERROR]: Failed to initialize GLAD\n" );
823 mErrorCode = OPENGL_ENGINE_ERROR_GLAD_INIT;
824 } else {
825 if(DEBUG) {
826 // Successfully loaded OpenGL
827 fprintf(stdout, "[INFO]: GLAD initialized v%d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
828 }
829 }
830#endif
831
832 if(mErrorCode == OPENGL_ENGINE_ERROR_NO_ERROR) {
833 glGetIntegerv(GL_MAJOR_VERSION, &mOpenGLMajorVersionCreated);
834 glGetIntegerv(GL_MINOR_VERSION, &mOpenGLMinorVersionCreated);
835 if(DEBUG) {
836 fprintf(stdout, "[INFO]: OpenGL v%d.%d requested\n", mOpenGLMajorVersionRequested, mOpenGLMinorVersionRequested);
837 fprintf(stdout, "[INFO]: OpenGL v%d.%d created\n", mOpenGLMajorVersionCreated, mOpenGLMinorVersionCreated);
838 }
839
840 GLint numExtensions = 0;
841 glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
842 for (int i = 0; i < numExtensions; i++) {
843 _extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)) );
844 }
845 }
846}
847
848inline
850{
851 if(DEBUG) fprintf( stdout, "[INFO]: ...closing window...\n" );
852 glfwDestroyWindow(mpWindow ); // close our window
853 mpWindow = nullptr;
854 if(DEBUG) fprintf( stdout, "[INFO]: ...closing GLFW.....\n" );
855 glfwTerminate();
856}
857
858inline
860{
861 if( !_isCleanedUp ) {
862 const std::chrono::steady_clock::time_point shutdownBegin = std::chrono::steady_clock::now();
863 if (DEBUG) fprintf(stdout, "\n[INFO]: Shutting down.......\n");
864
865 const std::chrono::steady_clock::time_point sceneBegin = std::chrono::steady_clock::now();
866 mCleanupScene(); // delete scene info from CPU
867 const std::chrono::steady_clock::time_point sceneEnd = std::chrono::steady_clock::now();
868 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupScene()");
869
870 const std::chrono::steady_clock::time_point fontsBegin = std::chrono::steady_clock::now();
871 mCleanupFonts(); // delete textures from GPU
872 const std::chrono::steady_clock::time_point fontsEnd = std::chrono::steady_clock::now();
873 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupFonts()");
874
875 const std::chrono::steady_clock::time_point texturesBegin = std::chrono::steady_clock::now();
876 mCleanupTextures(); // delete textures from GPU
877 const std::chrono::steady_clock::time_point texturesEnd = std::chrono::steady_clock::now();
878 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupTextures()");
879
880 const std::chrono::steady_clock::time_point buffersBegin = std::chrono::steady_clock::now();
881 mCleanupBuffers(); // delete VAOs/VBOs from GPU
882 const std::chrono::steady_clock::time_point buffersEnd = std::chrono::steady_clock::now();
883 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupBuffers()");
884
885 const std::chrono::steady_clock::time_point shadersBegin = std::chrono::steady_clock::now();
886 mCleanupShaders(); // delete shaders from GPU
887 const std::chrono::steady_clock::time_point shadersEnd = std::chrono::steady_clock::now();
888 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupShaders()");
889
890 const std::chrono::steady_clock::time_point openGLBegin = std::chrono::steady_clock::now();
891 mCleanupOpenGL(); // cleanup anything OpenGL related
892 _cleanupGLFunctions(); // cleanup anything function pointer related
893 const std::chrono::steady_clock::time_point openGLEnd = std::chrono::steady_clock::now();
894 CSCI441::OpenGLUtils::checkOpenGLErrors("post mCleanupOpenGL()");
895
896 const std::chrono::steady_clock::time_point glfwBegin = std::chrono::steady_clock::now();
897 mCleanupGLFW(); // shut down GLFW to clean up our context
898 const std::chrono::steady_clock::time_point glfwEnd = std::chrono::steady_clock::now();
899
900 const std::chrono::steady_clock::time_point shutdownEnd = std::chrono::steady_clock::now();
901 if (DEBUG) {
902 fprintf(stdout, "[INFO]: ..shut down complete!\n");
903 fprintf(stdout, "[INFO]: Shutdown: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shutdownEnd - shutdownBegin).count() ) / 1000000.0);
904 fprintf(stdout, "[INFO]: \tScene: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(sceneEnd - sceneBegin ).count() ) / 1000000.0);
905 fprintf(stdout, "[INFO]: \tFonts: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(fontsEnd - fontsBegin ).count() ) / 1000000.0);
906 fprintf(stdout, "[INFO]: \tTextures: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(texturesEnd - texturesBegin).count() ) / 1000000.0);
907 fprintf(stdout, "[INFO]: \tBuffers: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(buffersEnd - buffersBegin ).count() ) / 1000000.0);
908 fprintf(stdout, "[INFO]: \tShaders: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(shadersEnd - shadersBegin ).count() ) / 1000000.0);
909 fprintf(stdout, "[INFO]: \tOpenGL: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(openGLEnd - openGLBegin ).count() ) / 1000000.0);
910 fprintf(stdout, "[INFO]: \tGLFW: %2.3fs\n", static_cast<double>( std::chrono::duration_cast<std::chrono::microseconds>(glfwEnd - glfwBegin ).count() ) / 1000000.0);
911 }
912 _isCleanedUp = true;
913 _isInitialized = false;
914 }
915}
916
917inline
919 GLFWwindow* pWindow,
920 const int width,
921 const int height
922) {
923 const auto pEngine = static_cast<OpenGLEngine *>(glfwGetWindowUserPointer(pWindow));
924 pEngine->setCurrentWindowSize(width, height);
926 CSCI441::FontUtils::setFontSize(1.0f / static_cast<GLfloat>(width), 1.0f / static_cast<GLfloat>(height) );
927
928 GLint framebufferWidth, framebufferHeight;
929 glfwGetFramebufferSize( pEngine->getWindow(), &framebufferWidth, &framebufferHeight );
930 pEngine->setCurrentFramebufferSize(framebufferWidth, framebufferHeight);
931}
932
933inline
935{
936 if (DEBUG) fprintf(stdout, "\n[INFO]: Removing old shaders...\n");
937 mCleanupShaders();
938 if (DEBUG) fprintf(stdout, "\n[INFO]: Reloading shaders...\n");
939 mSetupShaders();
940 if (DEBUG) fprintf(stdout, "\n[INFO]: Shaders reloaded\n");
941}
942
943inline
945 const GLdouble currentTime = glfwGetTime();
946 ++_numFrames;
947 GLdouble fps = ( !_frameTimes.empty() ? _frameTimes.back() : 0.0 );
948 if (currentTime - _lastFrameTime > 0.1667) {
949 fps = static_cast<GLdouble>(_numFrames)/(currentTime - _lastFrameTime);
950 _numFrames = 0;
951 _lastFrameTime = currentTime;
952
953 if (_frameTimes.size() >= 10) _frameTimes.pop_front();
954 _frameTimes.push_back( fps );
955 }
956 return fps;
957}
958
959inline
961 GLdouble totalFPS = 0;
962 for( const auto& fpsUnit : _frameTimes ) {
963 totalFPS += fpsUnit;
964 }
965 return totalFPS / static_cast<GLdouble>(_frameTimes.size());
966}
967
968#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:241
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:293
int mOpenGLMinorVersion
the minor version of the requested OpenGL context
Definition: OpenGLEngine.hpp:257
int mOpenGLMajorVersion
the major version of the requested OpenGL context
Definition: OpenGLEngine.hpp:252
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:720
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:622
virtual void shutdown()
Cleanup everything needed for OpenGL Rendering. This includes freeing memory for data used in: any Sc...
Definition: OpenGLEngine.hpp:859
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:387
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:918
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:420
virtual void mCleanupScene()
override to cleanup any scene specific information
Definition: OpenGLEngine.hpp:405
virtual void mSetupFonts()
override to register any fonts with the GPU
Definition: OpenGLEngine.hpp:393
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:425
virtual void mCleanupTextures()
override to cleanup any textures from the GPU
Definition: OpenGLEngine.hpp:415
virtual GLdouble clockFrame() final
measures the amount of time elapsed since the last time this method was called
Definition: OpenGLEngine.hpp:944
bool mWindowResizable
if the GLFW window can be resized while open
Definition: OpenGLEngine.hpp:302
int mOpenGLMinorVersionRequested
the minor version of the requested OpenGL context
Definition: OpenGLEngine.hpp:269
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:297
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:410
int mOpenGLMajorVersionRequested
the major version of the requested OpenGL context
Definition: OpenGLEngine.hpp:263
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:318
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:246
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:323
char * mWindowTitle
the title of the GLFW window
Definition: OpenGLEngine.hpp:306
int mWindowHeight
the window height of the requested GLFW window
Definition: OpenGLEngine.hpp:289
virtual GLdouble fpsAverage() final
Definition: OpenGLEngine.hpp:960
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:274
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:934
virtual void mSetupShaders()
override to register any shaders with the GPU
Definition: OpenGLEngine.hpp:375
GLFWwindow * mpWindow
pointer to the GLFW window object
Definition: OpenGLEngine.hpp:310
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:705
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:381
virtual ~OpenGLEngine()
cleans up our OpenGL Engine by destroying the OpenGL context, GLFW window, and cleaning up all GPU re...
Definition: OpenGLEngine.hpp:557
virtual void mCleanupGLFW()
Destroys the associated GLFW window and terminates the GLFW instance.
Definition: OpenGLEngine.hpp:849
int mOpenGLMinorVersionCreated
the minor version of the created OpenGL context
Definition: OpenGLEngine.hpp:279
int mWindowWidth
the window width of the requested GLFW window
Definition: OpenGLEngine.hpp:284
virtual void mCleanupOpenGL()
override to cleanup any specific OpenGL features
Definition: OpenGLEngine.hpp:430
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:756
virtual void mSetupScene()
override to setup any scene specific information
Definition: OpenGLEngine.hpp:399
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