19/src/Camera.cpp
2023-08-19 13:08:45 +03:00

191 lines
5.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Camera.h"
#include <GLM/gtc/matrix_transform.hpp>
#include <GLM/ext/matrix_transform.hpp>
// Границы каскадов
const float camera_cascade_distances[] = {CAMERA_NEAR, CAMERA_FAR / 50.0f, CAMERA_FAR / 10.0f, CAMERA_FAR / 3.0f, CAMERA_FAR};
// Защищенный (protected) конструктор камеры без перспективы
Camera::Camera(const glm::vec3 &pos, const glm::vec2 &xyOffset)
: position(pos), currentRotation(xyOffset)
{
sensitivity = 0.05;
recalcTarget();
}
// Конструктор камеры с проекцией перспективы
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec2 &xyOffset, float fovy)
: Camera(position, xyOffset)
{
setPerspective(fovy, aspect);
}
// Конструктор ортографической камеры
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec2 &xyOffset)
: Camera(position, xyOffset)
{
setOrtho(width, height);
}
// Деструктор
Camera::~Camera() { }
// Пересчет цели, на которую смотрит камера
void Camera::recalcTarget()
{
if(currentRotation.y > 89.0f)
currentRotation.y = 89.0f;
if(currentRotation.y < -89.0f)
currentRotation.y = -89.0f;
target.x = cos(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
target.y = sin(glm::radians(currentRotation.y));
target.z = sin(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
requiredRecalcView = true;
requiredRecalcVP = true;
}
// Пересчет матрицы вида
void Camera::recalcView()
{
view = glm::lookAt(position, position + target, CAMERA_UP_VECTOR);
requiredRecalcView = false;
}
// Пересчет произведения матриц
void Camera::recalcVP()
{
vp = projection * view;
requiredRecalcVP = false;
}
// Возвращает ссылку на константную матрицу проекции
const glm::mat4& Camera::getProjection()
{
return projection;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getView()
{
if (requiredRecalcView)
recalcView();
return view;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getVP()
{
if (requiredRecalcVP)
{
if (requiredRecalcView)
recalcView();
recalcVP();
}
return vp;
}
// Поворачивает камеру на dx и dy пикселей
void Camera::rotate(const glm::vec2 &xyOffset)
{
currentRotation += xyOffset * sensitivity;
recalcTarget();
requiredRecalcView = true;
requiredRecalcVP = true;
requiredRecalcCoords = true;
}
// Сдвигает камеру на указанный вектор (dx,dy,dz)
void Camera::move(const glm::vec3 &posOffset)
{
position += posOffset;
requiredRecalcView = true;
requiredRecalcVP = true;
requiredRecalcCoords = true;
}
// Устанавливает местоположение
void Camera::setPosition(const glm::vec3 &pos)
{
position = pos;
requiredRecalcView = true;
requiredRecalcVP = true;
requiredRecalcCoords = true;
}
// Устанавливает угол поворота камеры
void Camera::setRotation(const glm::vec2 &xyOffset)
{
currentRotation = xyOffset;
recalcTarget();
requiredRecalcCoords = true;
}
// Устанавливает заданную матрицу перспективы
void Camera::setPerspective(float fovy, float aspect)
{
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = true;
requiredRecalcCoords = true;
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
cascade_proj[cascade] = glm::perspective(glm::radians(fovy), aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
}
// Устанавливает заданную ортографическую матрицу
void Camera::setOrtho(float width, float height)
{
const float aspect = width / height;
projection = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = true;
requiredRecalcCoords = true;
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
cascade_proj[cascade] = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
}
// Изменяет чувствительность мыши
void Camera::setSensitivity(float sens)
{
sensitivity = sens;
}
// Данные о камере для шейдера
CameraData& Camera::getData()
{
static CameraData data;
data = {getProjection(), getView(), position};
return data;
}
// Доступ к координатам проекции
const glm::vec4 (*Camera::getProjCoords())[8]
{
if (requiredRecalcCoords)
{
glm::vec4 typical_points[8] = { { 1, 1, 1,1}
, { 1, 1,-1,1}
, { 1,-1, 1,1}
, { 1,-1,-1,1}
, {-1, 1, 1,1}
, {-1, 1,-1,1}
, {-1,-1, 1,1}
, {-1,-1,-1,1}};
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
{
glm::mat4 inv = glm::inverse(cascade_proj[cascade] * getView());
for (int i = 0; i < 8; i++)
{
coords[cascade][i] = inv * typical_points[i];
coords[cascade][i] /= coords[cascade][i].w;
}
}
requiredRecalcCoords = false;
}
return coords;
}