247 lines
6.4 KiB
C++
247 lines
6.4 KiB
C++
// Voxspatium Engine - Voxel Planets engine
|
|
// Copyright (C) 2018 Evert "Diamond" Prants <evert@lunasqu.ee>
|
|
//
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
#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<glm::vec3> 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<glm::vec3> &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));
|
|
}
|