Files
Hoops_Exchange/exchange/exchangesource/MeshViewer/application/application.cpp
2025-12-15 23:22:33 +08:00

466 lines
26 KiB
C++

/***********************************************************************************************************************
*
* 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 <iostream>
#include <fstream>
#include <glm/gtc/matrix_transform.hpp>
/*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<float>() / 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<float>( xposIn );
const float ypos = static_cast<float>( 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;
}