#include "opengl-game.hpp" #include #include "consts.hpp" #include "logger.hpp" #include "utils.hpp" using namespace std; OpenGLGame::OpenGLGame() { gui = nullptr; window = nullptr; } OpenGLGame::~OpenGLGame() { } void OpenGLGame::run(int width, int height, unsigned char guiFlags) { #ifdef NDEBUG cout << "DEBUGGING IS OFF" << endl; #else cout << "DEBUGGING IS ON" << endl; #endif cout << "OpenGL Game" << endl; // TODO: Refactor the logger api to be more flexible, // esp. since gl_log() and gl_log_err() have issues printing anything besides stirngs restart_gl_log(); gl_log("starting GLFW\n%s", glfwGetVersionString()); open_log(); get_log() << "starting GLFW" << endl; get_log() << glfwGetVersionString() << endl; if (initWindow(width, height, guiFlags) == RTWO_ERROR) { return; } initOpenGL(); mainLoop(); cleanup(); close_log(); } // TODO: Make some more initi functions, or call this initUI if the // amount of things initialized here keeps growing bool OpenGLGame::initWindow(int width, int height, unsigned char guiFlags) { // TODO: Put all fonts, textures, and images in the assets folder gui = new GameGui_GLFW(); if (gui->init() == RTWO_ERROR) { // TODO: Also print these sorts of errors to the log cout << "UI library could not be initialized!" << endl; cout << gui->getError() << endl; return RTWO_ERROR; } cout << "GUI init succeeded" << endl; window = (GLFWwindow*) gui->createWindow("OpenGL Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN); if (window == nullptr) { cout << "Window could not be created!" << endl; cout << gui->getError() << endl; return RTWO_ERROR; } viewport = { 0, 0, gui->getWindowWidth(), gui->getWindowHeight() }; cout << "Target window size: (" << width << ", " << height << ")" << endl; cout << "Actual window size: (" << viewport.width << ", " << viewport.height << ")" << endl; return RTWO_SUCCESS; } void OpenGLGame::initOpenGL() { glfwMakeContextCurrent(window); glViewport(0, 0, gui->getWindowWidth(), gui->getWindowHeight()); glewExperimental = GL_TRUE; glewInit(); if (GLEW_KHR_debug) { cout << "FOUND GLEW debug extension" << endl; glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback((GLDEBUGPROC)opengl_debug_callback, nullptr); cout << "Bound debug callback" << endl; } else { cout << "OpenGL debug message callback is not supported" << endl; } // Setup Dear ImGui binding IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui_ImplGlfwGL3_Init(window, true); // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); // The glfw event handlers have to be bound after ImGui is initialized. // Otherwise, it seems they get overridden by ImGui ((GameGui_GLFW*)gui)->bindEventHandlers(); graphicsPipelines.push_back(GraphicsPipeline_OpenGL(viewport)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::points)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::colors)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::normals)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_OBJECT_VARYING, 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset)); graphicsPipelines.back().createPipeline("gl-shaders/ship.vert", "gl-shaders/ship.frag"); graphicsPipelines.push_back(GraphicsPipeline_OpenGL(viewport)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::points)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::colors)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::normals)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_OBJECT_VARYING, 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset)); graphicsPipelines.back().createPipeline("gl-shaders/asteroid.vert", "gl-shaders/asteroid.frag"); graphicsPipelines.push_back(GraphicsPipeline_OpenGL(viewport)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&SceneObject::points)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 2, GL_FLOAT, offset_of(&SceneObject::texcoords)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_OBJECT_VARYING, 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset)); graphicsPipelines.back().createPipeline("gl-shaders/laser.vert", "gl-shaders/laser.frag"); graphicsPipelines.push_back(GraphicsPipeline_OpenGL(viewport)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 3, GL_FLOAT, offset_of(&ParticleEffect::particleVelocities)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_POINT_VARYING, 1, GL_FLOAT, offset_of(&ParticleEffect::particleTimes)); graphicsPipelines.back().addVaryingAttribute(ATTRIB_OBJECT_VARYING, 1, GL_UNSIGNED_INT, offset_of(&ParticleEffect::ubo_offset)); graphicsPipelines.back().createPipeline("gl-shaders/explosion.vert", "gl-shaders/explosion.frag"); cout << "Created " << graphicsPipelines.size() << " graphics pipelines" << endl; } void OpenGLGame::mainLoop() { UIEvent uiEvent; bool quit = false; while (!quit) { gui->processEvents(); while (gui->pollEvent(&uiEvent)) { GameEvent& e = uiEvent.event; switch (e.type) { case UI_EVENT_QUIT: cout << "Quit event detected" << endl; quit = true; break; case UI_EVENT_KEYDOWN: if (e.key.keycode == GLFW_KEY_ESCAPE) { quit = true; } else { cout << "Key event detected" << endl; } break; case UI_EVENT_WINDOWRESIZE: cout << "Window resize event detected" << endl; viewport.width = e.windowResize.width; viewport.height = e.windowResize.height; break; default: cout << "Unhandled UI event: " << e.type << endl; } } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Anton's book suggests placing this here, after glClear(). Check it's impact on framerate // TODO: This doesn't seem to work correctly when in the loop. DO some research // max viewport dims are clamped to glGet(GL_MAX_VIEWPORT_DIMS) //glViewport(0, 0, gui->getWindowWidth(), gui->getWindowHeight()); renderScene(); renderUI(); glfwSwapBuffers(window); } } void OpenGLGame::renderScene() { } void OpenGLGame::renderUI() { ImGui_ImplGlfwGL3_NewFrame(); { int padding = 4; ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once); ImGui::SetNextWindowSize(ImVec2(gui->getWindowWidth() + 2 * padding, gui->getWindowHeight() + 2 * padding), ImGuiCond_Always); ImGui::Begin("WndMain", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); ImGui::InvisibleButton("", ImVec2(10, 80)); ImGui::InvisibleButton("", ImVec2(285, 18)); ImGui::SameLine(); ImGui::Button("New Game"); ImGui::InvisibleButton("", ImVec2(10, 15)); ImGui::InvisibleButton("", ImVec2(300, 18)); ImGui::SameLine(); ImGui::Button("Quit"); ImGui::End(); } ImGui::Render(); ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData()); } void OpenGLGame::cleanup() { ImGui_ImplGlfwGL3_Shutdown(); ImGui::DestroyContext(); gui->destroyWindow(); gui->shutdown(); delete gui; } void APIENTRY opengl_debug_callback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam ) { string strMessage(message); // TODO: Use C++ strings directly and see if there are other ways to clean // this function up char source_str[2048]; char type_str[2048]; char severity_str[2048]; switch (source) { case 0x8246: strcpy(source_str, "API"); break; case 0x8247: strcpy(source_str, "WINDOW_SYSTEM"); break; case 0x8248: strcpy(source_str, "SHADER_COMPILER"); break; case 0x8249: strcpy(source_str, "THIRD_PARTY"); break; case 0x824A: strcpy(source_str, "APPLICATION"); break; case 0x824B: strcpy(source_str, "OTHER"); break; default: strcpy(source_str, "undefined"); break; } switch (type) { case 0x824C: strcpy(type_str, "ERROR"); break; case 0x824D: strcpy(type_str, "DEPRECATED_BEHAVIOR"); break; case 0x824E: strcpy(type_str, "UNDEFINED_BEHAVIOR"); break; case 0x824F: strcpy(type_str, "PORTABILITY"); break; case 0x8250: strcpy(type_str, "PERFORMANCE"); break; case 0x8251: strcpy(type_str, "OTHER"); break; case 0x8268: strcpy(type_str, "MARKER"); break; case 0x8269: strcpy(type_str, "PUSH_GROUP"); break; case 0x826A: strcpy(type_str, "POP_GROUP"); break; default: strcpy(type_str, "undefined"); break; } switch (severity) { case 0x9146: strcpy(severity_str, "HIGH"); break; case 0x9147: strcpy(severity_str, "MEDIUM"); break; case 0x9148: strcpy(severity_str, "LOW"); break; case 0x826B: strcpy(severity_str, "NOTIFICATION"); break; default: strcpy(severity_str, "undefined"); break; } if (string(severity_str) != "NOTIFICATION") { cout << "OpenGL Error!!!" << endl; cout << "Source: " << string(source_str) << endl; cout << "Type: " << string(type_str) << endl; cout << "Severity: " << string(severity_str) << endl; cout << strMessage << endl; } }