You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
799 lines
28 KiB
799 lines
28 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.
|
|
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <glad/glad.h>
|
|
#include <rs-config.h>
|
|
#include "ux-window.h"
|
|
|
|
#include <imgui.h>
|
|
#include <imgui_impl_glfw.h>
|
|
|
|
#include "device-model.h"
|
|
|
|
#include <rsutils/os/special-folder.h>
|
|
#include "os.h"
|
|
|
|
// We use STB image to load the splash-screen from memory
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include <stb_image.h>
|
|
// int-rs-splash.hpp contains the PNG image from res/int-rs-splash.png
|
|
#include "res/int-rs-splash.hpp"
|
|
#include "res/icon.h"
|
|
|
|
#include "ux-alignment.h"
|
|
|
|
#include <opengl3.h>
|
|
|
|
#include <iostream>
|
|
|
|
void glfw_error_callback(int error, const char* description)
|
|
{
|
|
std::cerr << "GLFW Driver Error: " << description << "\n";
|
|
}
|
|
|
|
namespace rs2
|
|
{
|
|
void GLAPIENTRY MessageCallback(GLenum source,
|
|
GLenum type,
|
|
GLuint id,
|
|
GLenum severity,
|
|
GLsizei length,
|
|
const GLchar* message,
|
|
const void* userParam)
|
|
{
|
|
if (type == GL_DEBUG_TYPE_ERROR)
|
|
{
|
|
fprintf(stderr, "GL CALLBACK: ** GL ERROR ** type = 0x%x, severity = 0x%x, message = %s\n",
|
|
type, severity, message);
|
|
}
|
|
}
|
|
|
|
void prepare_config_file()
|
|
{
|
|
config_file::instance().set_default(configurations::update::allow_rc_firmware, false);
|
|
config_file::instance().set_default(configurations::update::recommend_calibration, true);
|
|
config_file::instance().set_default(configurations::update::recommend_updates, true);
|
|
config_file::instance().set_default(configurations::update::sw_updates_url, server_versions_db_url);
|
|
config_file::instance().set_default(configurations::update::sw_updates_official_server, true);
|
|
|
|
config_file::instance().set_default(configurations::window::is_fullscreen, false);
|
|
config_file::instance().set_default(configurations::window::saved_pos, false);
|
|
config_file::instance().set_default(configurations::window::saved_size, false);
|
|
config_file::instance().set_default(configurations::window::font_size, 16);
|
|
|
|
config_file::instance().set_default(configurations::viewer::is_measuring, false);
|
|
config_file::instance().set_default(configurations::viewer::log_to_console, true);
|
|
config_file::instance().set_default(configurations::viewer::log_to_file, false);
|
|
config_file::instance().set_default(configurations::viewer::log_severity, 2);
|
|
config_file::instance().set_default(configurations::viewer::metric_system, true);
|
|
config_file::instance().set_default(configurations::viewer::ground_truth_r, 2500);
|
|
config_file::instance().set_default(configurations::viewer::dashboard_open, true);
|
|
|
|
config_file::instance().set_default(configurations::record::compression_mode, 2); // Let the device decide
|
|
config_file::instance().set_default(configurations::record::file_save_mode, 0); // Auto-select name
|
|
|
|
config_file::instance().set_default(configurations::performance::show_fps, false);
|
|
config_file::instance().set_default(configurations::performance::vsync, true);
|
|
config_file::instance().set_default(configurations::performance::occlusion_invalidation, true);
|
|
|
|
config_file::instance().set_default(configurations::ply::mesh, true);
|
|
config_file::instance().set_default(configurations::ply::use_normals, false);
|
|
config_file::instance().set_default(configurations::ply::encoding, configurations::ply::binary);
|
|
|
|
config_file::instance().set_default(configurations::viewer::commands_xml, "./Commands.xml");
|
|
config_file::instance().set_default(configurations::viewer::hwlogger_xml, "./HWLoggerEvents.xml");
|
|
|
|
std::string path;
|
|
try
|
|
{
|
|
path = rsutils::os::get_special_folder( rsutils::os::special_folder::user_documents );
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
std::string msg = "Failed to get Documents folder";
|
|
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
|
|
path = "";
|
|
}
|
|
config_file::instance().set_default(configurations::viewer::log_filename, path + "librealsense.log");
|
|
config_file::instance().set_default(configurations::record::default_path, path);
|
|
|
|
#ifdef __APPLE__
|
|
|
|
config_file::instance().set_default(configurations::performance::font_oversample, 2);
|
|
config_file::instance().set_default(configurations::performance::enable_msaa, true);
|
|
config_file::instance().set_default(configurations::performance::msaa_samples, 4);
|
|
// On Mac-OS, mixing OpenGL 2 with OpenGL 3 is not supported by the driver
|
|
// while this can be worked-around, this will take more development time,
|
|
// so for now Macs should not use the GLSL stuff
|
|
config_file::instance().set_default(configurations::performance::glsl_for_processing, false);
|
|
config_file::instance().set_default(configurations::performance::glsl_for_rendering, false);
|
|
#else
|
|
auto vendor = (const char*)glGetString(GL_VENDOR);
|
|
auto renderer = (const char*)glGetString(GL_RENDERER);
|
|
auto version = (const char*)glGetString(GL_VERSION);
|
|
auto glsl = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
|
|
|
bool use_glsl = false;
|
|
|
|
// Absolutely arbitrary list of manufacturers that are likely to benefit from GLSL optimisation
|
|
if (starts_with(rsutils::string::to_lower(vendor), "intel") ||
|
|
starts_with(rsutils::string::to_lower(vendor), "ati") ||
|
|
starts_with(rsutils::string::to_lower(vendor), "nvidia"))
|
|
{
|
|
use_glsl = true;
|
|
}
|
|
|
|
// Double-check that GLSL 1.3+ is supported
|
|
if (starts_with(rsutils::string::to_lower(vendor), "1.1") || starts_with(rsutils::string::to_lower(vendor), "1.2"))
|
|
{
|
|
use_glsl = false;
|
|
}
|
|
|
|
if (use_glsl)
|
|
{
|
|
config_file::instance().set_default(configurations::performance::show_skybox, true);
|
|
config_file::instance().set_default(configurations::performance::font_oversample, 2);
|
|
config_file::instance().set_default(configurations::performance::enable_msaa, false);
|
|
config_file::instance().set_default(configurations::performance::msaa_samples, 2);
|
|
config_file::instance().set_default(configurations::performance::glsl_for_processing, true);
|
|
config_file::instance().set_default(configurations::performance::glsl_for_rendering, true);
|
|
config_file::instance().set_default(configurations::viewer::shading_mode, 2);
|
|
}
|
|
else
|
|
{
|
|
config_file::instance().set_default(configurations::performance::show_skybox, false);
|
|
config_file::instance().set_default(configurations::performance::font_oversample, 1);
|
|
config_file::instance().set_default(configurations::performance::enable_msaa, false);
|
|
config_file::instance().set_default(configurations::performance::msaa_samples, 2);
|
|
config_file::instance().set_default(configurations::performance::glsl_for_processing, false);
|
|
config_file::instance().set_default(configurations::performance::glsl_for_rendering, false);
|
|
config_file::instance().set_default(configurations::viewer::shading_mode, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ux_window::reload()
|
|
{
|
|
_reload = true;
|
|
}
|
|
|
|
void ux_window::refresh()
|
|
{
|
|
if (_use_glsl_proc) rs2::gl::shutdown_processing();
|
|
rs2::gl::shutdown_rendering();
|
|
|
|
_use_glsl_render = config_file::instance().get(configurations::performance::glsl_for_rendering);
|
|
_use_glsl_proc = config_file::instance().get(configurations::performance::glsl_for_processing);
|
|
|
|
rs2::gl::init_rendering(_use_glsl_render);
|
|
if (_use_glsl_proc) rs2::gl::init_processing(_win, _use_glsl_proc);
|
|
}
|
|
|
|
void ux_window::link_hovered()
|
|
{
|
|
_link_hovered = true;
|
|
}
|
|
|
|
void ux_window::cross_hovered()
|
|
{
|
|
_cross_hovered = true;
|
|
}
|
|
|
|
void ux_window::setup_icon()
|
|
{
|
|
GLFWimage icon[4];
|
|
|
|
int x, y, comp;
|
|
|
|
auto icon_16 = stbi_load_from_memory(icon_16_png_data, (int)icon_16_png_size, &x, &y, &comp, false);
|
|
icon[0].width = x; icon[0].height = y;
|
|
icon[0].pixels = icon_16;
|
|
|
|
auto icon_24 = stbi_load_from_memory(icon_24_png_data, (int)icon_24_png_size, &x, &y, &comp, false);
|
|
icon[1].width = x; icon[1].height = y;
|
|
icon[1].pixels = icon_24;
|
|
|
|
auto icon_64 = stbi_load_from_memory(icon_64_png_data, (int)icon_64_png_size, &x, &y, &comp, false);
|
|
icon[2].width = x; icon[2].height = y;
|
|
icon[2].pixels = icon_64;
|
|
|
|
auto icon_256 = stbi_load_from_memory(icon_256_png_data, (int)icon_256_png_size, &x, &y, &comp, false);
|
|
icon[3].width = x; icon[3].height = y;
|
|
icon[3].pixels = icon_256;
|
|
|
|
glfwSetWindowIcon(_win, 4, icon);
|
|
|
|
stbi_image_free(icon_16);
|
|
stbi_image_free(icon_24);
|
|
stbi_image_free(icon_64);
|
|
stbi_image_free(icon_256);
|
|
}
|
|
|
|
|
|
void ux_window::open_window()
|
|
{
|
|
if (_win)
|
|
{
|
|
rs2::gl::shutdown_rendering();
|
|
if (_use_glsl_proc) rs2::gl::shutdown_processing();
|
|
|
|
ImGui::GetIO().Fonts->ClearFonts(); // To be refactored into Viewer theme object
|
|
ImGui_ImplGlfw_Shutdown();
|
|
glfwDestroyWindow(_win);
|
|
glfwDestroyCursor(_hand_cursor);
|
|
glfwDestroyCursor(_cross_cursor);
|
|
glfwTerminate();
|
|
}
|
|
|
|
if (!glfwInit())
|
|
exit(1);
|
|
|
|
glfwSetErrorCallback(glfw_error_callback);
|
|
|
|
_hand_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
|
_cross_cursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
|
|
|
|
{
|
|
glfwWindowHint(GLFW_VISIBLE, 0);
|
|
auto ctx = glfwCreateWindow(640, 480, "Offscreen Context", nullptr, nullptr);
|
|
if (!ctx) throw std::runtime_error("Could not initialize offscreen context!");
|
|
glfwMakeContextCurrent(ctx);
|
|
|
|
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
|
|
|
// OpenGL 2.1 backward-compatibility fixes.
|
|
// On macOS, the compatibility profile is OpenGL 2.1 + extensions.
|
|
if (!GLAD_GL_VERSION_3_0 && !GLAD_GL_ARB_vertex_array_object) {
|
|
if (GLAD_GL_APPLE_vertex_array_object) {
|
|
glBindVertexArray = glBindVertexArrayAPPLE;
|
|
glDeleteVertexArrays = glDeleteVertexArraysAPPLE;
|
|
glGenVertexArrays = glGenVertexArraysAPPLE;
|
|
glIsVertexArray = glIsVertexArrayAPPLE;
|
|
} else {
|
|
throw std::runtime_error("OpenGL 3.0 or ARB_vertex_array_object extension required!");
|
|
}
|
|
}
|
|
|
|
prepare_config_file();
|
|
|
|
glfwDestroyWindow(ctx);
|
|
}
|
|
|
|
_use_glsl_render = config_file::instance().get(configurations::performance::glsl_for_rendering);
|
|
_use_glsl_proc = config_file::instance().get(configurations::performance::glsl_for_processing);
|
|
|
|
_enable_msaa = config_file::instance().get(configurations::performance::enable_msaa);
|
|
_msaa_samples = config_file::instance().get(configurations::performance::msaa_samples);
|
|
|
|
_fullscreen = config_file::instance().get(configurations::window::is_fullscreen);
|
|
|
|
rs2_error* e = nullptr;
|
|
_title_str = rsutils::string::from() << _title << " v" << api_version_to_string(rs2_get_api_version(&e));
|
|
auto debug = is_debug();
|
|
if (debug)
|
|
{
|
|
_title_str = _title_str + ", DEBUG";
|
|
}
|
|
|
|
_width = 1024;
|
|
_height = 768;
|
|
|
|
// Dynamically adjust new window size (by detecting monitor resolution)
|
|
auto primary = glfwGetPrimaryMonitor();
|
|
if (primary)
|
|
{
|
|
const auto mode = glfwGetVideoMode(primary);
|
|
if (_fullscreen)
|
|
{
|
|
_width = mode->width;
|
|
_height = mode->height;
|
|
}
|
|
else
|
|
{
|
|
_width = int(mode->width * 0.7f);
|
|
_height = int(mode->height * 0.7f);
|
|
}
|
|
}
|
|
|
|
if (_enable_msaa)
|
|
glfwWindowHint(GLFW_SAMPLES, _msaa_samples);
|
|
|
|
glfwWindowHint(GLFW_VISIBLE, 0);
|
|
|
|
// Create GUI Windows
|
|
_win = glfwCreateWindow(_width, _height, _title_str.c_str(),
|
|
(_fullscreen ? primary : nullptr), nullptr);
|
|
if (!_win)
|
|
throw std::runtime_error("Could not open OpenGL window, please check your graphic drivers or use the textual SDK tools");
|
|
|
|
if (config_file::instance().get(configurations::window::saved_pos))
|
|
{
|
|
int x = config_file::instance().get(configurations::window::position_x);
|
|
int y = config_file::instance().get(configurations::window::position_y);
|
|
|
|
int count;
|
|
GLFWmonitor** monitors = glfwGetMonitors(&count);
|
|
if (count > 0)
|
|
{
|
|
bool legal_position = false;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
auto rect = get_monitor_rect(monitors[i]);
|
|
if (rect.contains({ (float)x, (float)y }))
|
|
{
|
|
legal_position = true;
|
|
}
|
|
}
|
|
if (legal_position) glfwSetWindowPos(_win, x, y);
|
|
}
|
|
}
|
|
|
|
if (config_file::instance().get(configurations::window::saved_size))
|
|
{
|
|
int w = config_file::instance().get(configurations::window::width);
|
|
int h = config_file::instance().get(configurations::window::height);
|
|
glfwSetWindowSize(_win, w, h);
|
|
|
|
if (config_file::instance().get(configurations::window::maximized))
|
|
glfwMaximizeWindow(_win);
|
|
}
|
|
|
|
glfwMakeContextCurrent(_win);
|
|
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
|
|
|
if (glDebugMessageCallback)
|
|
{
|
|
// During init, enable debug output
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glDebugMessageCallback(MessageCallback, 0);
|
|
}
|
|
|
|
glfwSetWindowPosCallback(_win, [](GLFWwindow* w, int x, int y)
|
|
{
|
|
config_file::instance().set(configurations::window::saved_pos, true);
|
|
config_file::instance().set(configurations::window::position_x, x);
|
|
config_file::instance().set(configurations::window::position_y, y);
|
|
});
|
|
|
|
glfwSetWindowSizeCallback( _win, []( GLFWwindow * window, int width, int height ) {
|
|
if( width > 0 && height > 0 )
|
|
{
|
|
config_file::instance().set( configurations::window::saved_size, true );
|
|
config_file::instance().set( configurations::window::width, width );
|
|
config_file::instance().set( configurations::window::height, height );
|
|
config_file::instance().set( configurations::window::maximized,
|
|
glfwGetWindowAttrib( window, GLFW_MAXIMIZED ) );
|
|
}
|
|
} );
|
|
|
|
setup_icon();
|
|
|
|
ImGui_ImplGlfw_Init(_win, true);
|
|
|
|
if (_use_glsl_render)
|
|
_2d_vis = std::make_shared<visualizer_2d>(std::make_shared<splash_screen_shader>());
|
|
|
|
// Load fonts to be used with the ImGui - TODO move to RAII
|
|
imgui_easy_theming(_font_dynamic, _font_18, _monofont, font_size);
|
|
|
|
// Register for UI-controller events
|
|
glfwSetWindowUserPointer(_win, this);
|
|
|
|
|
|
glfwSetCursorPosCallback(_win, [](GLFWwindow* w, double cx, double cy)
|
|
{
|
|
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
|
|
data->_mouse.cursor = { (float)cx / data->_scale_factor,
|
|
(float)cy / data->_scale_factor };
|
|
});
|
|
glfwSetMouseButtonCallback(_win, [](GLFWwindow* w, int button, int action, int mods)
|
|
{
|
|
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
|
|
data->_mouse.mouse_down[0] = (button == GLFW_MOUSE_BUTTON_1) && (action != GLFW_RELEASE);
|
|
data->_mouse.mouse_down[1] = (button == GLFW_MOUSE_BUTTON_2) && (action != GLFW_RELEASE);
|
|
});
|
|
glfwSetScrollCallback(_win, [](GLFWwindow * w, double xoffset, double yoffset)
|
|
{
|
|
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
|
|
data->_mouse.mouse_wheel = static_cast<int>(yoffset);
|
|
data->_mouse.ui_wheel += static_cast<int>(yoffset);
|
|
});
|
|
|
|
glfwSetDropCallback(_win, [](GLFWwindow* w, int count, const char** paths)
|
|
{
|
|
auto data = reinterpret_cast<ux_window*>(glfwGetWindowUserPointer(w));
|
|
|
|
if (count <= 0) return;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
data->on_file_drop(paths[i]);
|
|
}
|
|
});
|
|
|
|
rs2::gl::init_rendering(_use_glsl_render);
|
|
if (_use_glsl_proc) rs2::gl::init_processing(_win, _use_glsl_proc);
|
|
|
|
glfwShowWindow(_win);
|
|
glfwFocusWindow(_win);
|
|
|
|
_show_fps = config_file::instance().get(configurations::performance::show_fps);
|
|
_vsync = config_file::instance().get(configurations::performance::vsync);
|
|
|
|
// Prepare the splash screen and do some initialization in the background
|
|
int x, y, comp;
|
|
auto r = stbi_load_from_memory(splash, (int)splash_size, &x, &y, &comp, false);
|
|
_splash_tex.upload_image(x, y, r);
|
|
stbi_image_free(r);
|
|
}
|
|
|
|
ux_window::ux_window(const char* title, context &ctx) :
|
|
_win(nullptr), _width(0), _height(0), _output_height(0),
|
|
_font_dynamic(nullptr), _font_18(nullptr), _monofont(nullptr), font_size(16), _app_ready(false),
|
|
_first_frame(true), _query_devices(true), _missing_device(false),
|
|
_hourglass_index(0), _dev_stat_message{}, _keep_alive(true), _title(title), _ctx(ctx)
|
|
{
|
|
open_window();
|
|
|
|
// Apply initial UI state
|
|
reset();
|
|
}
|
|
|
|
void ux_window::add_on_load_message(const std::string& msg)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
|
|
_on_load_message.push_back(msg);
|
|
}
|
|
|
|
void ux_window::imgui_config_pop()
|
|
{
|
|
ImGui::PopFont();
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopStyleVar(2);
|
|
end_frame();
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
void ux_window::imgui_config_push()
|
|
{
|
|
glPushMatrix();
|
|
glViewport(0, 0, _fb_width, _fb_height);
|
|
glClearColor(0.036f, 0.044f, 0.051f, 1.f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glLoadIdentity();
|
|
glOrtho(0, _width, _height, 0, -1, +1);
|
|
|
|
// Fade-in the logo
|
|
auto opacity = smoothstep(float(_splash_timer.get_elapsed_ms()), 100.f, 2500.f);
|
|
auto ox = 0.7f - smoothstep(float(_splash_timer.get_elapsed_ms()), 200.f, 1900.f) * 0.4f;
|
|
auto oy = 0.5f;
|
|
auto power = std::sin(smoothstep(float(_splash_timer.get_elapsed_ms()), 150.f, 2200.f) * 3.14f) * 0.96f;
|
|
|
|
if (_use_glsl_render)
|
|
{
|
|
auto shader = ((splash_screen_shader*)&_2d_vis->get_shader());
|
|
shader->begin();
|
|
shader->set_power(power);
|
|
shader->set_ray_center(float2{ ox, oy });
|
|
shader->end();
|
|
_2d_vis->draw_texture(_splash_tex.get_gl_handle(), opacity);
|
|
}
|
|
else
|
|
{
|
|
_splash_tex.show({ 0.f,0.f,float(_width),float(_height) }, opacity);
|
|
}
|
|
|
|
std::string hourglass = u8"\uf251";
|
|
static rsutils::time::periodic_timer every_200ms(std::chrono::milliseconds(200));
|
|
bool do_200ms = every_200ms;
|
|
if (_query_devices && do_200ms)
|
|
{
|
|
_missing_device = _ctx.query_devices(RS2_PRODUCT_LINE_ANY_INTEL).size() == 0;
|
|
_hourglass_index = (_hourglass_index + 1) % 4;
|
|
|
|
if (!_missing_device)
|
|
{
|
|
_dev_stat_message = u8"\uf287 RealSense device detected.";
|
|
_query_devices = false;
|
|
}
|
|
}
|
|
|
|
hourglass[2] += _hourglass_index;
|
|
|
|
auto flags = ImGuiWindowFlags_NoResize |
|
|
ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoCollapse |
|
|
ImGuiWindowFlags_NoTitleBar;
|
|
|
|
auto text_color = light_grey;
|
|
text_color.w = opacity;
|
|
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
|
|
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, white);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 5, 5 });
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 1);
|
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, transparent);
|
|
ImGui::SetNextWindowPos({ (float)_width / 2 - 150, (float)_height / 2 + 70 });
|
|
|
|
ImGui::SetNextWindowSize({ (float)_width, (float)_height });
|
|
ImGui::Begin("Splash Screen Banner", nullptr, flags);
|
|
ImGui::PushFont(_font_18);
|
|
|
|
ImGui::Text("%s Loading %s...", hourglass.c_str(), _title_str.c_str());
|
|
}
|
|
|
|
// Check that the graphic subsystem is valid and start a new frame
|
|
ux_window::operator bool()
|
|
{
|
|
end_frame();
|
|
|
|
if (_show_fps)
|
|
{
|
|
std::stringstream temp_title;
|
|
temp_title << _title_str;
|
|
|
|
auto fps = ImGui::GetIO().Framerate;
|
|
|
|
temp_title << ", FPS: " << fps;
|
|
glfwSetWindowTitle(_win, temp_title.str().c_str());
|
|
}
|
|
|
|
// Yield the CPU
|
|
if (!_vsync)
|
|
{
|
|
std::this_thread::yield();
|
|
glfwSwapInterval(0);
|
|
}
|
|
else
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
|
|
auto res = !glfwWindowShouldClose(_win);
|
|
|
|
|
|
if (_first_frame)
|
|
{
|
|
assert(!_first_load.joinable()); // You must call to reset() before initiate new thread
|
|
|
|
|
|
_first_load = std::thread([&]() {
|
|
while (_keep_alive && !_app_ready)
|
|
{
|
|
try
|
|
{
|
|
_app_ready = on_load();
|
|
}
|
|
catch (...)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::seconds(1)); // Wait for connect event and retry
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// If we are just getting started, render the Splash Screen instead of normal UI
|
|
while (res && (!_app_ready || _splash_timer.get_elapsed_ms() < 2000.f))
|
|
{
|
|
res = !glfwWindowShouldClose(_win);
|
|
glfwPollEvents();
|
|
|
|
begin_frame();
|
|
|
|
if (_first_frame)
|
|
{
|
|
_is_ui_aligned = is_gui_aligned(_win);
|
|
_first_frame = false;
|
|
}
|
|
|
|
imgui_config_push();
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
|
|
if (_on_load_message.empty())
|
|
{
|
|
ImGui::Text("%s", _dev_stat_message.c_str());
|
|
}
|
|
else if (!_on_load_message.empty())
|
|
{
|
|
ImGui::Text("%s", _dev_stat_message.c_str());
|
|
for (auto& msg : _on_load_message)
|
|
{
|
|
auto is_last_msg = (msg == _on_load_message.back());
|
|
if (is_last_msg)
|
|
ImGui::Text("%s", msg.c_str());
|
|
else if (!is_last_msg)
|
|
ImGui::Text("%s", msg.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
imgui_config_pop();
|
|
|
|
|
|
// Yield the CPU
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
|
|
// reset graphic pipe
|
|
begin_frame();
|
|
|
|
if (_link_hovered)
|
|
glfwSetCursor(_win, _hand_cursor);
|
|
else if (_cross_hovered)
|
|
glfwSetCursor(_win, _cross_cursor);
|
|
else
|
|
glfwSetCursor(_win, nullptr);
|
|
_cross_hovered = false;
|
|
_link_hovered = false;
|
|
_hovers_any_input_window = false;
|
|
|
|
return res;
|
|
}
|
|
|
|
ux_window::~ux_window()
|
|
{
|
|
if (_first_load.joinable())
|
|
{
|
|
_keep_alive = false;
|
|
_first_load.join();
|
|
}
|
|
|
|
end_frame();
|
|
|
|
try
|
|
{
|
|
rs2::gl::shutdown_rendering();
|
|
if (_use_glsl_proc) rs2::gl::shutdown_processing();
|
|
}
|
|
catch( ... )
|
|
{
|
|
}
|
|
|
|
ImGui::GetIO().Fonts->ClearFonts(); // To be refactored into Viewer theme object
|
|
ImGui_ImplGlfw_Shutdown();
|
|
glfwDestroyWindow(_win);
|
|
|
|
glfwDestroyCursor(_hand_cursor);
|
|
glfwDestroyCursor(_cross_cursor);
|
|
|
|
glfwTerminate();
|
|
}
|
|
|
|
void ux_window::begin_frame()
|
|
{
|
|
glfwPollEvents();
|
|
|
|
int state = glfwGetKey(_win, GLFW_KEY_F8);
|
|
if (state == GLFW_PRESS)
|
|
{
|
|
_fullscreen_pressed = true;
|
|
}
|
|
else
|
|
{
|
|
if (_fullscreen_pressed)
|
|
{
|
|
_fullscreen = !_fullscreen;
|
|
config_file::instance().set(configurations::window::is_fullscreen, _fullscreen);
|
|
open_window();
|
|
}
|
|
_fullscreen_pressed = false;
|
|
}
|
|
|
|
if (_reload)
|
|
{
|
|
open_window();
|
|
_reload = false;
|
|
on_reload_complete();
|
|
}
|
|
|
|
int w = _width; int h = _height;
|
|
|
|
glfwGetWindowSize(_win, &_width, &_height);
|
|
|
|
// Set minimum size 1
|
|
if (_width <= 0) _width = 1;
|
|
if (_height <= 0) _height = 1;
|
|
|
|
int fw = _fb_width;
|
|
int fh = _fb_height;
|
|
|
|
glfwGetFramebufferSize(_win, &_fb_width, &_fb_height);
|
|
|
|
// Set minimum size 1
|
|
if (_fb_width <= 0) _fb_width = 1;
|
|
if (_fb_height <= 0) _fb_height = 1;
|
|
|
|
if (fw != _fb_width || fh != _fb_height)
|
|
{
|
|
std::string msg = rsutils::string::from() << "Framebuffer size changed to " << _fb_width << " x " << _fb_height;
|
|
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
|
|
}
|
|
|
|
auto sf = _scale_factor;
|
|
|
|
// Update the scale factor each frame
|
|
// based on resolution and physical display size
|
|
_scale_factor = static_cast<float>(pick_scale_factor(_win));
|
|
_width = static_cast<int>(_width / _scale_factor);
|
|
_height = static_cast<int>(_height / _scale_factor);
|
|
|
|
if (w != _width || h != _height)
|
|
{
|
|
std::string msg = rsutils::string::from() << "Window size changed to " << _width << " x " << _height;
|
|
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
|
|
}
|
|
|
|
if (_scale_factor != sf)
|
|
{
|
|
std::string msg = rsutils::string::from() << "Scale Factor is now " << _scale_factor;
|
|
rs2::log(RS2_LOG_SEVERITY_INFO, msg.c_str());
|
|
}
|
|
|
|
// Reset ImGui state
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
ImGui::GetIO().MouseWheel = _mouse.ui_wheel;
|
|
_mouse.ui_wheel = 0.f;
|
|
|
|
ImGui_ImplGlfw_NewFrame(_scale_factor);
|
|
//ImGui::NewFrame();
|
|
}
|
|
|
|
void ux_window::begin_viewport()
|
|
{
|
|
// Rendering
|
|
glViewport(0, 0,
|
|
static_cast<int>(ImGui::GetIO().DisplaySize.x * _scale_factor),
|
|
static_cast<int>(ImGui::GetIO().DisplaySize.y * _scale_factor));
|
|
|
|
if (_enable_msaa) glEnable(GL_MULTISAMPLE);
|
|
else glDisable(GL_MULTISAMPLE);
|
|
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
void ux_window::end_frame()
|
|
{
|
|
if (!_first_frame)
|
|
{
|
|
ImGui::Render();
|
|
|
|
glfwSwapBuffers(_win);
|
|
_mouse.mouse_wheel = 0;
|
|
}
|
|
}
|
|
|
|
void ux_window::reset()
|
|
{
|
|
if (_first_load.joinable())
|
|
{
|
|
_keep_alive = false;
|
|
_first_load.join();
|
|
_keep_alive = true;
|
|
}
|
|
|
|
_query_devices = true;
|
|
_missing_device = false;
|
|
_hourglass_index = 0;
|
|
_first_frame = true;
|
|
_app_ready = false;
|
|
_splash_timer.reset();
|
|
_dev_stat_message = u8"\uf287 Please connect Intel RealSense device!";
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(_on_load_message_mtx);
|
|
_on_load_message.clear();
|
|
}
|
|
}
|
|
|
|
}
|