Files
2025-12-15 23:22:33 +08:00

322 lines
21 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 "camera.h"
#include <glm/gtc/matrix_transform.hpp>
/**********************************************************************************************************************/
/*initialize_position: this function must be call once just after the camera has been created. It calculates the */
/* camera's position with the the model's bounding box. the initial position corresponds to an */
/* isometric view. The various member parameters are also initialized */
/*in : -bbox, used to calculate the appropriate camera position */
/*out: -orbital_center, the point which will be the center of the bounding, it also will be the orbital center of the */
/* camera movement */
/* -max_bbx_length, the diagonal length of the bounding box */
/* -position, the 3D point where the camera is located in the global space */
/* -up, the 3D vector of the camera which shows the up direction of the camera from his position */
/* -right, the 3D vector which shows the right direction of the camera from his position */
/* -front, the 3D vector which shows the front direction of the camera from his position, normal vector from */
/* position to the orbital center */
/* -start_position, 3D point of the initial position, allow the user to come back at the first view */
/* -start_right, 3D vector of the initial right vector, allow the user to come back at the first view */
/* -start_front, 3D vector of the initial front vector, allow the user to come back at the first view */
/* -start_up, 3D vector of the initial front vector, allow the user to come back at the first view */
/* -current_view, this setting is initialized to the isometric view, then we could switch step by step to the */
/* other views */
/*return : void */
/**********************************************************************************************************************/
void Camera::initialize_position(const utils::geometry::BoundingBox& bbox)
{
orbital_center = bbox.center();
max_bbx_length = (float)glm::distance(bbox.min, bbox.max);
position = orbital_center - (max_bbx_length * 1.5f) * glm::vec3(-glm::sqrt(2.f) / 2.f, glm::sqrt(2.f) / 2.f, 0.f);
current_view = EView::ISO;
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(glm::sqrt(2.f) / 2.f, glm::sqrt(2.f) / 2.f, 0.f);
front = glm::vec3(-glm::sqrt(2.f) / 2.f, glm::sqrt(2.f) / 2.f, 0.f);
start_position = position;
start_right = right;
start_front = front;
start_up = up;
}
/**********************************************************************************************************************/
/*get_view_matrix: a wrapper to the glm lookAt function which builds a "look at" view matrix based on the camera */
/* parameters. */
/*in :-void */
/*out:-void */
/*return: A glm::mat4 matrix corresponding to the "look at" of the camera position */
/**********************************************************************************************************************/
glm::mat4 Camera::get_view_matrix()
{
return glm::lookAt(position, position + front, up);
}
/**********************************************************************************************************************/
/*calculate_far: called by compute_projection_matrix in the main loop of the drawing, it is */
/* particularly useful when zooming, and the far plan will move closer or further away, then the model */
/* won't be cut off. */
/*in :-void */
/*out:-void */
/*return: a float which will be the new far to apply in the rendering model */
/**********************************************************************************************************************/
float Camera::calculate_far()
{
float far = glm::distance(orbital_center, position) + max_bbx_length / 2.f;
far *= 1.1f;
return far;
}
/**********************************************************************************************************************/
/*calculate_far: called by compute_projection_matrix in the main loop of the drawing, it is */
/* particularly useful when the distance is very great, the near performs better whenit is closer to */
/* the model */
/*in :-void */
/*out:-void */
/*return: a float which will be the new near to apply to the rendering model */
/**********************************************************************************************************************/
float Camera::calculate_near()
{
float near = glm::distance(orbital_center, position) - max_bbx_length / 2.f;
near *= 0.9f;
if (near <= 0)
{
near = max_bbx_length/1000.f;
}
return near;
}
/**********************************************************************************************************************/
/* rotate_camera: private method to rotate the model, the camera is actually moved around the axis (axe_rotate), */
/* passing through the centre of the orbitand through an angle alpha(in parameter). */
/*in : -axe_rotate, the vector qualifying the axis passing through the orbital centre around which the camera will */
/* rotate */
/* -alpha, a float defining the angle of rotation */
/*out: -position, the point defining the new camera position after the rotation */
/* -front, the front vector after the rotation */
/* -right, the right vector after the rotation */
/* -up, the right vector after the rotation */
/*return: void */
/**********************************************************************************************************************/
void Camera::rotate_camera(glm::vec3 axe_rotate, float alpha)
{
glm::mat4 rotate = glm::mat4(1.f);
glm::vec4 translate(-orbital_center.x, -orbital_center.y, -orbital_center.z, 1.f);
rotate = glm::rotate(rotate, alpha, axe_rotate);
position = glm::vec4(position, 1.f) + translate;
position = glm::vec4(position, 1.f) * rotate;
position = glm::vec4(position, 1.f) - translate;
front = glm::vec4(front, 1.f) * rotate;
right = glm::vec4(right, 1.f) * rotate;
up = glm::vec4(up, 1.f) * rotate;
}
/**********************************************************************************************************************/
/*set_to_origin: called after pressing the "O" button, to place the camera in the position it was in when the model */
/* was opened. */
/*in : -void */
/*out: -position, the point defining the camera position will be defined as it was at the begining */
/* -right, the right vector of the camera will be defined as it was at the beginning */
/* -front, the front vector of the camera will be defined as it was at the beginning */
/* -up, the up vector of the camera will be defined as it was at the beginning */
/*return: void */
/**********************************************************************************************************************/
void Camera::set_to_origin()
{
position = start_position;
right = start_right;
front = start_front;
up = start_up;
}
/**********************************************************************************************************************/
/*process_keyboard: called after a keyboard event to move the camera by rotation or translation */
/*in : -direction, an EMovement enabling to know how to move the camera */
/*out: -position, the 3D point of the camera will be changed with the appropriate move */
/* -front (indirectly by rotate_camera), the front camera vector after the move */
/* -right (indirectly by rotate_camera), the right camera vector after the move */
/* -up (indirectly by rotate_camera), the up camera vector after the move */
/*return: void */
/**********************************************************************************************************************/
void Camera::process_keyboard(Camera::EMovement direction)
{
switch (direction)
{
case Camera::EMovement::ZOOM_IN:
position += front * (max_bbx_length * 0.05f);
break;
case Camera::EMovement::ZOOM_OUT:
position -= front * (max_bbx_length * 0.05f);
break;
case Camera::EMovement::FRONT_DIRECT:
rotate_camera(front, glm::radians(1.f));
break;
case Camera::EMovement::FRONT_INV:
rotate_camera(front, glm::radians(-1.f));
break;
case Camera::EMovement::RIGHT_DIRECT:
rotate_camera(right, glm::radians(1.f));
break;
case Camera::EMovement::RIGHT_INV:
rotate_camera(right, glm::radians(-1.f));
break;
case Camera::EMovement::UP_DIRECT:
rotate_camera(up, glm::radians(1.f));
break;
case Camera::EMovement::UP_INV:
rotate_camera(up, glm::radians(-1.f));
break;
case Camera::EMovement::MV_RIGHT:
position -= right * (max_bbx_length * 0.05f);
break;
case Camera::EMovement::MV_LEFT:
position += right * (max_bbx_length * 0.05f);
break;
case Camera::EMovement::MV_UP:
//we lower the camera to move the model up
position -= up * (max_bbx_length * 0.05f);
break;
case Camera::EMovement::MV_DOWN:
position += up * (max_bbx_length * 0.05f);
break;
default:
break;
}
}
/**********************************************************************************************************************/
/*process_mouse_rotation: called by a mouse move event with his left button pressed and not release, this functions */
/* allows the user to move the 3D model by rotation around right, front, or up axes depending */
/* */
/*in : -xOffset, the mouse move in X axes since the last capture of the mouse position */
/* -yOffset, the mouse move in Y axes since the last capture of the mouse position */
/* -screenWidth, the current width of the opengl window where the model is displayed */
/* -screenHeight, the current length of the opengl window where the model is displayed */
/*out: -position (indirectly by rotate_camera), the 3D point of the camera will be changed with the appropriate move */
/* -front (indirectly by rotate_camera), the front camera vector after the move */
/* -right (indirectly by rotate_camera), the right camera vector after the move */
/* -up (indirectly by rotate_camera), the up camera vector after the move */
/*return: void */
/**********************************************************************************************************************/
void Camera::process_mouse_rotation(float xOffset, float yOffset, int screenWidth, int screenHeight)
{
if (glm::abs(screenWidth) > glm::epsilon<float>() && glm::abs(screenHeight) > glm::epsilon<float>())
{
const float alphaX = xOffset / screenWidth * glm::pi<float>();
const float alphaY = yOffset / screenHeight * glm::pi<float>();
rotate_camera(up, alphaX);
rotate_camera(right, -alphaY);
}
}
/**********************************************************************************************************************/
/*process_mouse_translation: called by a mouse move event with his left button pressed and not release simultaneously */
/* with the shift key presses, this functions allows the user to move the 3D model by */
/* parallel translation with the screen */
/* */
/*in : -xOffset, the mouse move in X axes since the last capture of the mouse position */
/* -yOffset, the mouse move in Y axes since the last capture of the mouse position */
/*out: -position (indirectly by rotate_camera), the 3D point of the camera will be changed with the appropriate move */
/* -front (indirectly by rotate_camera), the front camera vector after the move */
/* -right (indirectly by rotate_camera), the right camera vector after the move */
/* -up (indirectly by rotate_camera), the up camera vector after the move */
/*return: void */
/**********************************************************************************************************************/
void Camera::process_mouse_translation(float xOffset, float yOffset)
{
position -= right * xOffset * max_bbx_length / 1000.f;
position -= up * yOffset * max_bbx_length / 1000.f;
}
/**********************************************************************************************************************/
/*process_mouse_scroll: called by a mouse scroll event, the purpose of this function is to move the camera closer or */
/* further away */
/*in : -is_zoom_in, a boolean to know if the camera has to move closer or further */
/*out: -position, the 3D point which will be the new position of the camea */
/*return: void */
/**********************************************************************************************************************/
void Camera::process_mouse_scroll(bool is_zoom_in)
{
float sign = is_zoom_in ? 2.f : -2.f;
position += sign * front * ( max_bbx_length * 0.05f );
}
/**********************************************************************************************************************/
/*process_switch_view: called by a mouse right click event, the purpose of this function is to swith the model view */
/* between the usual view know: Isometric, top, bottom, left, right,front and back */
/*in : void */
/*out: -position, the 3D point of the new position of the camera in the switched view */
/* -up, the 3D vector of the up camera in the switched view */
/* -right, the 3D vector of the right camera in the switched view */
/* -front, the 3D vector of the front camera in the switched view */
/*return: void */
/**********************************************************************************************************************/
void Camera::process_switch_view()
{
current_view = (Camera::EView)(((int)current_view + 1) % 7);
switch (current_view)
{
case Camera::EView::ISO:
position = orbital_center - (max_bbx_length * 1.5f) * glm::vec3(-glm::sqrt(2.f)/2.f, glm::sqrt(2.f) / 2.f, 0.f);
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(glm::sqrt(2.f) / 2.f, glm::sqrt(2.f) / 2.f, 0.f);
front = glm::vec3(-glm::sqrt(2.f) / 2.f, glm::sqrt(2.f) / 2.f, 0.f);
break;
case Camera::EView::TOP:
position = orbital_center + (max_bbx_length * 1.5f) * glm::vec3(0.f, 0.f, 1.f);
up = glm::vec3(0.f, 1.f, 0.f);
right = glm::vec3(-1.f, 0.f, 0.f);
front = glm::vec3(0.f, 0.f, -1.f);
break;
case Camera::EView::BOTTOM:
position = orbital_center - (max_bbx_length * 1.5f) * glm::vec3(0.f, 0.f, 1.f);
up = glm::vec3(0.f, -1.f, 0.f);
right = glm::vec3(1.f, 0.f, 0.f);
front = glm::vec3(0.f, 0.f, 1.f);
break;
case Camera::EView::LEFT:
position = orbital_center - (max_bbx_length * 1.5f) * glm::vec3(1.f, 0.f, 0.f);
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(0.f, -1.f, 0.f);
front = glm::vec3(1.f, 0.f, 0.f);
break;
case Camera::EView::RIGHT:
position = orbital_center + (max_bbx_length * 1.5f) * glm::vec3(1.f, 0.f, 0.f);
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(0.f, 1.f, 0.f);
front = glm::vec3(-1.f, 0.f, 0.f);
break;
case Camera::EView::FRONT:
position = orbital_center - (max_bbx_length * 1.5f) * glm::vec3(0.f, 1.f, 0.f);
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(1.f, 0.f, 0.f);
front = glm::vec3(0.f, 1.f, 0.f);
break;
case Camera::EView::BACK:
position = orbital_center + (max_bbx_length * 1.5f) * glm::vec3(0.f, 1.f, 0.f);
up = glm::vec3(0.f, 0.f, 1.f);
right = glm::vec3(-1.f, 0.f, 0.f);
front = glm::vec3(0.f, -1.f, 0.f);
break;
default:
break;
}
}