// Voxspatium Engine - Voxel Planets engine // Copyright (C) 2018 Evert "Diamond" Prants // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "Camera.h" #include "util/Log.h" #include "Application.h" // TODO: use Roll Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch, GLfloat roll) : m_front(glm::vec3(0.0f, 0.0f, -1.0f)), m_movementSpeed(SPEED), m_mouseSensitivity(SENSITIVTY), m_zoom(ZOOM) { m_position = position; m_worldUp = up; m_yaw = yaw; m_pitch = pitch; m_roll = roll; updateCameraVectors(); updateProjection(); } Camera::Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch, GLfloat roll) : m_front(glm::vec3(0.0f, 0.0f, -1.0f)), m_movementSpeed(SPEED), m_mouseSensitivity(SENSITIVTY), m_zoom(ZOOM) { m_position = glm::vec3(posX, posY, posZ); m_worldUp = glm::vec3(upX, upY, upZ); m_yaw = yaw; m_pitch = pitch; m_roll = roll; updateCameraVectors(); updateProjection(); } Camera::~Camera() { } glm::mat4 Camera::getViewMatrix() { return glm::lookAt(m_position, m_position + m_front, m_up); } void Camera::updateFrustum(glm::mat4 modelMatrix) { const glm::mat4 mvp = getViewMatrix() * m_projection * modelMatrix; // Extract the clipping planes; right, left, far, near, top, bottom m_planes[0] = mvp[3] - mvp[0]; m_planes[1] = mvp[3] + mvp[0]; m_planes[2] = mvp[3] - mvp[2]; m_planes[3] = mvp[3] + mvp[2]; m_planes[4] = mvp[3] - mvp[1]; m_planes[5] = mvp[3] + mvp[1]; // Normalize vectors for (unsigned int i = 0; i < 6; i++) { m_planes[i] = glm::normalize(m_planes[i]); } } // Calculates the closest distance from a given point to a given clipping plane float Camera::frustumDistanceToPlane(const int plane, const glm::vec3 &point) const { return m_planes[plane].x * point.x + m_planes[plane].y * point.y + m_planes[plane].z * point.z + m_planes[plane].w; } bool Camera::frustumContainsPoint(const glm::vec3 &point) const { // For each plane; return outside if the point is behind the plane for (unsigned int i = 0; i < 6; i++) { if (frustumDistanceToPlane(i, point) <= 0.0) return false; } // Return inside return true; } int Camera::frustumContainsSphere(const glm::vec3 &position, const double radius) const { // Plane counter int planeCount = 0; // Use the point-to-plane distance to calculate the number of planes the sphere is in front of for (unsigned int i = 0; i < 6; i++) { const double distance = frustumDistanceToPlane(i, position); if (distance <= -radius) return 0; else if (distance > radius) planeCount++; } // Return inside if in front of all planes; otherwise intersecting return planeCount == 6 ? 1 : 2; } int Camera::frustumContainsBox(const glm::vec3 &min, const glm::vec3 &max) const { // Build a vector holding all box corners std::vector points; for (unsigned int i = 0; i < 8; i++) { points.push_back(glm::vec3(i < 4 ? max.x : min.x, i % 4 < 2 ? max.y : min.y, i % 2 ? max.z : min.z)); } // Test the box as a polygon return frustumContainsPolygon(points); } int Camera::frustumContainsPolygon(const std::vector &points) const { // Plane counter int planeCount = 0; // Use the point-to-plane distance to calculate the number of planes the polygon is in front of for (unsigned int i = 0; i < 6; i++) { unsigned int pointCount = 0; for (unsigned int j = 0; j < points.size(); j++) { if (frustumDistanceToPlane(i, points[j]) > 0.0) pointCount++; if (pointCount == 0) return 0; else if (pointCount == points.size()) planeCount++; } } // Return inside if in front of all planes; otherwise intersecting return planeCount == 6 ? 1 : 2; } void Camera::shaderViewProjection(Shader& shader) { shader.setUniform("viewMatrix", getViewMatrix()); shader.setUniform("projectionMatrix", m_projection); } void Camera::processKeyboard(Camera_Movement direction, GLfloat deltaTime) { GLfloat velocity = m_movementSpeed * deltaTime; if (direction == CAM_FORWARD) m_position += m_front * velocity; if (direction == CAM_BACKWARD) m_position -= m_front * velocity; if (direction == CAM_LEFT) m_position -= m_right * velocity; if (direction == CAM_RIGHT) m_position += m_right * velocity; } void Camera::processMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true) { xoffset *= m_mouseSensitivity; yoffset *= m_mouseSensitivity; m_yaw += xoffset; m_pitch += yoffset; // Make sure that when pitch is out of bounds, screen doesn't get flipped if (constrainPitch) { if (m_pitch > 89.0f) { m_pitch = 89.0f; } if (m_pitch < -89.0f) { m_pitch = -89.0f; } } // Update Front, Right and Up Vectors using the updated Eular angles updateCameraVectors(); } void Camera::processMouseScroll(GLfloat yoffset) { // if (m_zoom >= 44.0f && m_zoom <= 45.0f) // { // m_zoom -= yoffset; // } // if (m_zoom <= 44.0f) // { // m_zoom = 44.0f; // } // if (m_zoom >= 45.0f) // { // m_zoom = 45.0f; // } // updateProjection(); m_movementSpeed += yoffset * 50.0f; } void Camera::updateProjection(void) { // Recalculate the projection matrix glm::vec2 screenDims = Application::getInstance().getScreenDimensions(); m_projection = glm::perspective(getFOV(), (GLfloat)screenDims.x/(GLfloat)screenDims.y, 0.1f, 10000.0f); } void Camera::updateCameraVectors(void) { // Calculate the new Front vector glm::vec3 front; front.x = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); front.y = sin(glm::radians(m_pitch)); front.z = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); m_front = glm::normalize(front); // Also re-calculate the Right and Up vector // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. m_right = glm::normalize(glm::cross(m_front, m_worldUp)); m_up = glm::normalize(glm::cross(m_right, m_front)); }