/*********************************************************************************************************************** * * 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 /**********************************************************************************************************************/ /*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() && glm::abs(screenHeight) > glm::epsilon()) { const float alphaX = xOffset / screenWidth * glm::pi(); const float alphaY = yOffset / screenHeight * glm::pi(); 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; } }