466 lines
26 KiB
C++
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;
|
|
} |