394 lines
14 KiB
C++
394 lines
14 KiB
C++
//=============================================================================
|
|
// 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 <algorithm>
|
|
|
|
#include <imgui_glfw.h>
|
|
#include <lato-font.h>
|
|
|
|
#if __EMSCRIPTEN__
|
|
#include <emscripten/emscripten.h>
|
|
#include <emscripten/html5.h>
|
|
#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
|
|
//=============================================================================
|