191 lines
5.8 KiB
C++
191 lines
5.8 KiB
C++
#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;
|
||
}
|