//============================================================================= // Copyright (C) 2011-2017 The pmp-library developers // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. //============================================================================= #include "Window.h" #include #include #include #if __EMSCRIPTEN__ #include #include #endif //============================================================================= namespace pmp { //============================================================================= Window* Window::m_instance = nullptr; //----------------------------------------------------------------------------- Window::Window(const char* title, int width, int height, bool showgui) : m_width(width), m_height(height), m_showImGUI(showgui), n_frames_(0.0), elapsed_time_(0.0) { // initialize glfw window if (!glfwInit()) exit(EXIT_FAILURE); // request modern OpenGL 3.2 window glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_SAMPLES, 8); #ifndef __APPLE__ glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); #endif m_window = glfwCreateWindow(width, height, title, nullptr, nullptr); if (!m_window) { glfwTerminate(); std::cerr << "Cannot create GLFW window.\n"; exit(EXIT_FAILURE); } glfwMakeContextCurrent(m_window); m_instance = this; // enable v-sync glfwSwapInterval(1); // now that we have a GL context, initialize GLEW glewExperimental = GL_TRUE; GLenum err = glewInit(); if (err != GLEW_OK) { std::cerr << "Error initializing GLEW: " << glewGetErrorString(err) << std::endl; exit(1); } // debug: print GL and GLSL version std::cout << "GLEW " << glewGetString(GLEW_VERSION) << std::endl; std::cout << "GL " << glGetString(GL_VERSION) << std::endl; std::cout << "GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; // call glGetError once to clear error queue glGetError(); // detect highDPI scaling int windowWidth, windowHeight, framebufferWidth, framebufferHeight; glfwGetWindowSize(m_window, &windowWidth, &windowHeight); glfwGetFramebufferSize(m_window, &framebufferWidth, &framebufferHeight); m_scaling = framebufferWidth / windowWidth; m_width = framebufferWidth; m_height = framebufferHeight; if (m_scaling != 1) std::cout << "highDPI scaling: " << m_scaling << std::endl; // register glfw callbacks glfwSetErrorCallback(glfwError); glfwSetCharCallback(m_window, glfwCharacter); glfwSetKeyCallback(m_window, glfwKeyboard); glfwSetCursorPosCallback(m_window, glfwMotion); glfwSetMouseButtonCallback(m_window, glfwMouse); glfwSetScrollCallback(m_window, glfwScroll); glfwSetFramebufferSizeCallback(m_window, glfwResize); // turn on multi-sampling to anti-alias lines #ifndef __EMSCRIPTEN__ glEnable(GL_MULTISAMPLE); GLint n_samples; glGetIntegerv(GL_SAMPLES, &n_samples); std::cout << "Multi-sampling uses " << n_samples << " per pixel\n"; #endif // setup imgui initImGUI(); } //----------------------------------------------------------------------------- void Window::initImGUI() { // High DPI scaling float sx, sy; glfwGetWindowContentScale(m_window, &sx, &sy); int imgui_scale = int(0.5*(sx+sy)); ImGui_Init(m_window, false); // load Lato font from pre-compiled ttf file ImFontConfig config; config.OversampleH = 2; config.OversampleV = 2; ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromMemoryCompressedTTF(LatoLatin_compressed_data, LatoLatin_compressed_size, imgui_scale * 14); // window style ImGuiStyle& style = ImGui::GetStyle(); style.WindowRounding = imgui_scale * 4.0f; style.FrameRounding = imgui_scale * 4.0f; style.GrabMinSize = imgui_scale * 10.0f; style.GrabRounding = imgui_scale * 4.0f; // color scheme adapted from // https://github.com/ocornut/imgui/pull/511#issuecomment-175719267 style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); style.Colors[ImGuiCol_WindowBg] = ImVec4(0.90f, 0.90f, 0.90f, 0.70f); style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); style.Colors[ImGuiCol_PopupBg] = ImVec4(0.90f, 0.90f, 0.90f, 0.90f); style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f); style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.16f, 0.62f, 0.87f, 0.40f); style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.16f, 0.62f, 0.87f, 0.67f); style.Colors[ImGuiCol_TitleBg] = ImVec4(0.16f, 0.62f, 0.87f, 0.80f); style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.16f, 0.62f, 0.87f, 0.40f); style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.62f, 0.87f, 0.80f); style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f); style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); style.Colors[ImGuiCol_ComboBg] = ImVec4(0.86f, 0.86f, 0.86f, 0.99f); style.Colors[ImGuiCol_CheckMark] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.16f, 0.62f, 0.87f, 0.78f); style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_Button] = ImVec4(0.16f, 0.62f, 0.87f, 0.40f); style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_Header] = ImVec4(0.16f, 0.62f, 0.87f, 0.31f); style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.16f, 0.62f, 0.87f, 0.80f); style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_Column] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.16f, 0.62f, 0.87f, 0.78f); style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.16f, 0.62f, 0.87f, 1.00f); style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f); style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.16f, 0.62f, 0.87f, 0.67f); style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.16f, 0.62f, 0.87f, 0.95f); style.Colors[ImGuiCol_CloseButton] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f); style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f); style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.16f, 0.62f, 0.87f, 0.35f); style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } //----------------------------------------------------------------------------- Window::~Window() { // terminate imgui ImGui_Shutdown(); // terminate GLFW glfwTerminate(); } //----------------------------------------------------------------------------- int Window::run() { #if __EMSCRIPTEN__ emscripten_set_main_loop(Window::render_frame, 0, 1); #else while (!glfwWindowShouldClose(m_window)) { Window::render_frame(); } #endif glfwDestroyWindow(m_window); return EXIT_SUCCESS; } //----------------------------------------------------------------------------- void Window::render_frame() { m_instance->fps_watch_.start(); #if __EMSCRIPTEN__ // determine correct canvas/framebuffer size int w, h, f; double dw, dh; emscripten_get_canvas_size(&w, &h, &f); emscripten_get_element_css_size(nullptr, &dw, &dh); if (w != int(dw) || h != int(dh)) { w = int(dw); h = int(dh); emscripten_set_canvas_size(w, h); glfwResize(m_instance->m_window, w, h); ; } #endif // do some computations m_instance->doProcessing(); // preapre and process ImGUI elements if (m_instance->showImGUI()) { ImGui_NewFrame(); ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_Once); ImGui::Begin( "Mesh Info", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize); m_instance->processImGUI(); ImGui::End(); } // draw scene m_instance->display(); // draw GUI if (m_instance->showImGUI()) { ImGui::Render(); } // swap buffers glfwSwapBuffers(m_instance->m_window); // handle events glfwPollEvents(); m_instance->elapsed_time_ += m_instance->fps_watch_.stop(); ++m_instance->n_frames_; if (m_instance->elapsed_time_ > 200.0) { m_instance->fps_ = (m_instance->n_frames_ / m_instance->elapsed_time_) * 1000.0; m_instance->n_frames_ = 0.0; m_instance->elapsed_time_ = 0.0; } } //----------------------------------------------------------------------------- void Window::glfwError(int error, const char* description) { std::cerr << "error (" << error << "):" << description << std::endl; } //----------------------------------------------------------------------------- void Window::glfwCharacter(GLFWwindow* window, unsigned int c) { ImGui_CharCallback(window, c); if (!ImGui::GetIO().WantCaptureKeyboard) { m_instance->character(c); } } //----------------------------------------------------------------------------- void Window::glfwKeyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { ImGui_KeyCallback(window, key, scancode, action, mods); if (!ImGui::GetIO().WantCaptureKeyboard) { m_instance->keyboard(key, scancode, action, mods); } } //----------------------------------------------------------------------------- void Window::glfwMotion(GLFWwindow* /*window*/, double xpos, double ypos) { // correct for highDPI scaling m_instance->motion(m_instance->m_scaling * xpos, m_instance->m_scaling * ypos); } //----------------------------------------------------------------------------- void Window::glfwMouse(GLFWwindow* window, int button, int action, int mods) { ImGui_MouseButtonCallback(window, button, action, mods); if (!ImGui::GetIO().WantCaptureMouse) { m_instance->mouse(button, action, mods); } } //----------------------------------------------------------------------------- void Window::glfwScroll(GLFWwindow* window, double xoffset, double yoffset) { #ifdef __EMSCRIPTEN__ yoffset *= -1.0; #endif ImGui_ScrollCallback(window, xoffset, yoffset); if (!ImGui::GetIO().WantCaptureMouse) { m_instance->scroll(xoffset, yoffset); } } //----------------------------------------------------------------------------- void Window::glfwResize(GLFWwindow* /*window*/, int width, int height) { m_instance->m_width = width; m_instance->m_height = height; m_instance->resize(width, height); } //----------------------------------------------------------------------------- void Window::cursorPos(double& x, double& y) const { glfwGetCursorPos(m_window, &x, &y); x *= m_instance->m_scaling; y *= m_instance->m_scaling; } //============================================================================= } // namespace pmp //=============================================================================