/*********************************************************************************************************************** * * Copyright (c) 2010 - 2025 by Tech Soft 3D, Inc. * The information contained herein is confidential and proprietary to Tech Soft 3D, Inc., and considered a trade secret * as defined under civil and criminal statutes. Tech Soft 3D shall pursue its civil and criminal remedies in the event * of unauthorized use or misappropriation of its trade secrets. Use of this information by anyone other than authorized * employees of Tech Soft 3D, Inc. is granted only under a written non-disclosure agreement, expressly prescribing the * scope and manner of such use. * ***********************************************************************************************************************/ #include "application.h" #include #include #include /*static settings initialisation*/ glm::vec2 Application::last_position = glm::vec2(0.f); bool Application::first_mouse = true; Camera Application::camera = Camera(); int Application::screen_width = 0; int Application::screen_height = 0; bool Application::is_wireframe = false; /*global variable*/ constexpr unsigned short icon_size = 32; /**********************************************************************************************************************/ /*compute_camera: wrapper to call initialize_position */ /*in : -bbox, used to calculate the appropriate camera position */ /*out: -void */ /*return: void */ /**********************************************************************************************************************/ void Application::compute_camera(const utils::geometry::BoundingBox& box) { Application::camera.initialize_position(box); } /**********************************************************************************************************************/ /*~Application: destructor of the Application'class, it calls glfwTerminate which destroys all remaining windows and */ /* cursorsand frees any other glfw allocated resources. */ /*in : -void */ /*out: -window, which is destroyed no further callbacks will be called for that window. */ /*return: void */ /**********************************************************************************************************************/ Application::~Application() { if (window != nullptr) { glfwDestroyWindow((GLFWwindow*) window); window = nullptr; } glfwTerminate(); } /**********************************************************************************************************************/ /*initialize_glf: This function initializes the GLFW library with initialize_glf. It also sets hints for the next call*/ /* to glfwCreateWindow */ /*in : -void */ /*out: -void */ /*return: 1 if the initialisation of glf failed, */ /* 0 if success */ /**********************************************************************************************************************/ int Application::initialize_glf() { if (glfwInit() != 1) { return 1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif return 0; } /**********************************************************************************************************************/ /*get_ts3d_logo: This function creates a ts3d logo from a binary file containing the RGB pixels of the ts3d logo */ /*in : -void */ /*out: -pixels a char array which will contain the ts3d logo in the following format: a sequence of 4 unsigned char */ /* for each pixel satisfying R G B A */ /*return: void */ /**********************************************************************************************************************/ void Application::get_ts3d_logo(char* pixels) { std::string logo_path = SHADERS_FOLDER; logo_path.append("/../../../resources/ts3d32.bin"); unsigned short pixel_size = icon_size * icon_size * 4; std::ifstream pixel_stream(logo_path.c_str(), std::ios::binary); pixel_stream.read(pixels, pixel_size); } /**********************************************************************************************************************/ /*create_window: This function creates a window and its associated OpenGL or OpenGL ES context. */ /*in : -void */ /*out: -screen_width, screen width initialisation */ /* -screen_height, screen height initialisation */ /* -last_position, set the last position of the mouse in the center of the screen */ /* -window, a GLFWwindow created here with its associated OpenGL context. */ /*return: 1 if the window creation fails */ /* 0 if success */ /**********************************************************************************************************************/ int Application::create_window(int width, int height, std::string title) { screen_width = width; screen_height = height; last_position.x = screen_width / 2.0f; last_position.y = screen_height / 2.0f; window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return 1; } glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); GLFWimage images[1]; unsigned short pixel_size = icon_size * icon_size * 4; char* pixels = new char[pixel_size]; get_ts3d_logo(pixels); images[0].pixels = (unsigned char*)pixels; images[0].width = icon_size; images[0].height = icon_size; glfwSetWindowIcon(window, 1, images); delete[] pixels; //else no logo in the window, but no error return 0; } void Application::set_blending(bool blend) { if (blend) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } } /**********************************************************************************************************************/ /*initialize_glad: Load all OpenGL functions using the glfw loader function at runtime. This is required to access */ /* functions from OpenGL. */ /*in : -void */ /*out: -void */ /*return: 1 if the OpenGL functions Loading fails */ /* 0 if success */ /**********************************************************************************************************************/ int Application::initialize_glad() { if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return 1; } //to be consistent with the initialisation of is_wireframe = false; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_DEPTH_TEST); return 0; } /**********************************************************************************************************************/ /*set_callbacks: initialise all the callback events that will interact with the user */ /*in : -void */ /*out: -void */ /*return: void */ /**********************************************************************************************************************/ void Application::set_callbacks() { glfwMakeContextCurrent(window); glfwSetCursorPosCallback(window, mouse_moved_callback); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, scroll_callback); glfwSetKeyCallback(window, key_pressed_callback); glfwSetWindowSizeCallback(window, window_size_changed_callback); glfwSetMouseButtonCallback(window, mouse_pressed_callback); // tell GLFW to capture our mouse glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } /**********************************************************************************************************************/ /*launch: calls all the initialisation functions */ /*in : -void */ /*out: -void */ /*return: 1 if there is an error in the launch */ /* 0 if sucess */ /**********************************************************************************************************************/ int Application::launch(int width, int height, std::string title) { if (initialize_glf() != 0) return 1; if (create_window(width, height, title) != 0) return 1; set_callbacks(); if (initialize_glad() != 0) return 1; return 0; } /**********************************************************************************************************************/ /*compute_projection_matrix: Creates a matrix for a symetric perspective-view frustum. */ /*in : -screen width */ /* -screen height */ /* -camera */ /*return: glm::mat4 representing the symetric perspective-view frustrum */ /* glm::mat4 null if the screen height worth 0glm::mat4 */ /**********************************************************************************************************************/ glm::mat4 Application::compute_projection_matrix() const { if (screen_height > 0) { return glm::perspective(glm::pi() / 4.f, (float) screen_width / (float) screen_height, camera.calculate_near(), camera.calculate_far()); } return glm::mat4(0.f); } /**********************************************************************************************************************/ /*compute_view_matrix: get the "look at" view matrix based on the camera */ /*in : -void */ /*out: -void */ /*return: glm::mat4 containing the "look at" matrix representing the 3D model */ /**********************************************************************************************************************/ glm::mat4 Application::compute_view_matrix() const { return camera.get_view_matrix(); } /**********************************************************************************************************************/ /*clear: specify clear values (light grey) for the color buffers and clear buffers to preset values */ /*in : -void */ /*out: -void */ /*return: void */ /**********************************************************************************************************************/ void Application::clear() { glClearColor(0.7f, 0.7f, 0.7f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } /**********************************************************************************************************************/ /*should_close: returns the value of the close flag of the window. */ /*in : -void */ /*out: -void */ /*return: true if the windows is closed else true */ /**********************************************************************************************************************/ bool Application::should_close() { return glfwWindowShouldClose(window); } /**********************************************************************************************************************/ /*display: This function swaps the front and back buffers of window when rendering with OpenGL and processes only */ /* those events that are already in the event queue and then returns immediately */ /*in : -window */ /*out: -void */ /*return: void */ /**********************************************************************************************************************/ void Application::display() { glfwSwapBuffers(window); glfwPollEvents(); } /**********************************************************************************************************************/ /*mouse_moved_callback: callback function called for the cursor position event of the specified window */ /*in : -window,a GLFWwindow* where the event comes from */ /* -xposIn, a double representing the abscissa of the mouse */ /* -yposIn, a double representing the ordinate of the mouse */ /*out: -last_position, the new mouse coordinates become the last position of the mouse */ /* -first_mouse, this boolean allows the event to know if it's the first time in the mouse moving we enter in this*/ /* function, then we initialise the first usefull settings of the move */ /* -camera, the Camera object is modified with is new position */ /*return: void */ /**********************************************************************************************************************/ void Application::mouse_moved_callback(GLFWwindow* window, double xposIn, double yposIn) { if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { const float xpos = static_cast( xposIn ); const float ypos = static_cast( yposIn ); if (first_mouse) { last_position.x = xpos; last_position.y = ypos; first_mouse = false; } const float xOffset = xpos - last_position.x; const float yOffset = last_position.y - ypos; // reversed since y-coordinates go from bottom to top int width = 0; int height = 0; glfwGetWindowSize(window, &width, &height); if (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) { camera.process_mouse_translation(xOffset, yOffset); } else { camera.process_mouse_rotation(xOffset, yOffset, width, height); } last_position.x = xpos; last_position.y = ypos; } } /**********************************************************************************************************************/ /*mouse_pressed_callback: callback function called when a press mouse button event appears in window */ /*in : -window, the GLFWwindow* window where appears this event */ /* -button, int that represents mouse button that was pressed or released. */ /* -action, int that represents GLFW_PRESS or GLFW_RELEASE */ /* -int mods, int that represents which modifier keys were held down (not use here) */ /*out: -camera, the Camera object is modified with is new position */ /*return: void */ /**********************************************************************************************************************/ void Application::mouse_pressed_callback(GLFWwindow* /*window*/, int button, int action, int /*mods*/) { if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) { camera.process_switch_view(); } if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { first_mouse = true; } } /**********************************************************************************************************************/ /*framebuffer_size_callback: callback function called for framebuffer size event, that enbles to adapt the 3D model */ /* with the new window size */ /*in : -window, the GLFWwindow* window where appears this event */ /* -w, int that represents the new width in pixel of windoax */ /* -h, int that represents the new height in pixel of windoax */ /*out: -void */ /*return: void */ /**********************************************************************************************************************/ void Application::framebuffer_size_callback(GLFWwindow* /*window*/, int w, int h) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, w, h); } /**********************************************************************************************************************/ /*scroll_callback: A scroll callback function called by scroll events */ /*in : -window, a GLFWwindow that recevied the event */ /* -xoffset, double representing the scroll offset along the x-axis */ /* -yoffset, double representing the scroll offset along the y-axis */ /*out: -camera, the Camera object is modified with is new position */ /*return: void */ /**********************************************************************************************************************/ void Application::scroll_callback(GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) { camera.process_mouse_scroll(yoffset > 0.f); } /**********************************************************************************************************************/ /*key_pressed_callback: a keyboard key callback function called by key pressed events */ /*in : -window, a GLFWwindow that recevied the event */ /* -key, int representing the keyboard key that was pressed or released. */ /* -scancode, int representing the system-specific scancode of the key. */ /* -action, int representing GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT. */ /* -mods, int representing bit field describing which modifier keys were held down. */ /*out: -camera, the Camera object is modified with is new position */ /*return: void */ /**********************************************************************************************************************/ void Application::key_pressed_callback(GLFWwindow* window, int key, int /*scancode*/, int action, int mods) { if (action == GLFW_RELEASE) { if (key == GLFW_KEY_F && mods == GLFW_MOD_SHIFT) { // flip the Wireframe mode if (is_wireframe) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } is_wireframe = !is_wireframe; } else if (key == GLFW_KEY_ESCAPE) { glfwSetWindowShouldClose(window, true); } else if (key == GLFW_KEY_O) { camera.set_to_origin(); } } else if (action == GLFW_REPEAT || action == GLFW_PRESS) { switch (key) { case GLFW_KEY_W: camera.process_keyboard(Camera::EMovement::ZOOM_IN); break; case GLFW_KEY_S: camera.process_keyboard(Camera::EMovement::ZOOM_OUT); break; case GLFW_KEY_A: camera.process_keyboard(Camera::EMovement::FRONT_DIRECT); break; case GLFW_KEY_D: camera.process_keyboard(Camera::EMovement::FRONT_INV); break; case GLFW_KEY_R: camera.process_keyboard(Camera::EMovement::RIGHT_DIRECT); break; case GLFW_KEY_V: camera.process_keyboard(Camera::EMovement::RIGHT_INV); break; case GLFW_KEY_Z: camera.process_keyboard(Camera::EMovement::UP_DIRECT); break; case GLFW_KEY_X: camera.process_keyboard(Camera::EMovement::UP_INV); break; case GLFW_KEY_RIGHT: camera.process_keyboard(Camera::EMovement::MV_RIGHT); break; case GLFW_KEY_LEFT: camera.process_keyboard(Camera::EMovement::MV_LEFT); break; case GLFW_KEY_UP: camera.process_keyboard(Camera::EMovement::MV_UP); break; case GLFW_KEY_DOWN: camera.process_keyboard(Camera::EMovement::MV_DOWN); break; default: break; } } } /**********************************************************************************************************************/ /*window_size_changed_callback: window size callbacks function called when the screen size as changed */ /*in : -window, a GLFWwindow that recevied the event */ /* -width, int of the new width, in screen coordinates, of the window */ /* -height, int of the new eight, in screen coordinates, of the window */ /*out: -screen_width, the new width screen */ /* -screen_width, the new height screen */ /*return: void */ /**********************************************************************************************************************/ void Application::window_size_changed_callback(GLFWwindow* /*window*/, int width, int height) { screen_width = width; screen_height = height; }