diff --git a/include/Camera.h b/include/Camera.h new file mode 100644 index 0000000..64912be --- /dev/null +++ b/include/Camera.h @@ -0,0 +1,58 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include +#include +#include + +#include "Model.h" + +// Ближняя граница области отсечения +#define CAMERA_NEAR 0.1f +// Дальняя граница области отсечения +#define CAMERA_FAR 100.0f +// Вектор, задающий верх для камеры +#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f) +// Вектор, задающий стандартный поворот углами Эйлера (в положительном направлении оси Z) +#define CAMERA_DEFAULT_ROTATION glm::vec3(0.0f, 180.0f, 0.0f) +// Стандартный угол обзора +#define CAMERA_FOVy 60.0f +// Стандартная чувствительность +#define CAMERA_DEFAULT_SENSIVITY 0.005f + +class Camera : public Node +{ + public: + Camera(float aspect, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION, float fovy = CAMERA_FOVy); // Конструктор камеры с проекцией перспективы + Camera(float width, float height, const glm::vec3 &position = glm::vec3(0.0f), const glm::vec3 &initialRotation = CAMERA_DEFAULT_ROTATION); // Конструктор ортографической камеры + Camera(const Camera& copy); // Конструктор копирования камеры + Camera& operator=(const Camera& other); // Оператор присваивания + virtual ~Camera(); // Деструктор + + const glm::mat4& getVP(); // Возвращает ссылку на константную матрицу произведения матриц вида и проекции + const glm::mat4& getProjection(); // Возвращает ссылку на константную матрицу проекции + const glm::mat4& getView(); // Возвращает ссылку на константную матрицу вида + + void rotate(const glm::vec2 &xyOffset); // Поворачивает камеру на dx и dy пикселей с учетом чувствительности + + void setPerspective(float fov, float aspect); // Устанавливает заданную матрицу перспективы + void setOrtho(float width, float height); // Устанавливает заданную ортографическую матрицу + void setSensitivity(float sensitivity); // Изменяет чувствительность мыши + const float& getSensitivity() const; // Возвращает чувствительность мыши + + protected: + Camera(const glm::vec3 &position, const glm::vec3 &initialRotation); // Защищенный (protected) конструктор камеры без перспективы + + glm::mat4 view; // Матрица вида + glm::mat4 projection; // Матрица проекции + glm::mat4 vp; // Матрица произведения вида и проекции + bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры + + float sensitivity; // Чувствительность мыши + + virtual void recalcMatrices(); // Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed +}; + + +#endif // CAMERA_H \ No newline at end of file diff --git a/src/Camera.cpp b/src/Camera.cpp new file mode 100644 index 0000000..9ebcdb2 --- /dev/null +++ b/src/Camera.cpp @@ -0,0 +1,160 @@ +#include "Camera.h" + +// Защищенный (protected) конструктор камеры без перспективы +Camera::Camera(const glm::vec3 &pos, const glm::vec3 &initialRotation) +{ + sensitivity = CAMERA_DEFAULT_SENSIVITY; + position = pos; // задаем позицию + // Определяем начальный поворот + glm::quat rotationAroundX = glm::angleAxis( glm::radians(initialRotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + glm::quat rotationAroundY = glm::angleAxis(-glm::radians(initialRotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::quat rotationAroundZ = glm::angleAxis( glm::radians(initialRotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + rotation = rotationAroundX * rotationAroundY * rotationAroundZ; + // Признак изменения + changed = true; +} + +// Конструктор камеры с проекцией перспективы +Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec3 &initialRotation, float fovy) +: Camera(position, initialRotation) +{ + setPerspective(fovy, aspect); +} + +// Конструктор ортографической камеры +Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec3 &initialRotation) +: Camera(position, initialRotation) +{ + setOrtho(width, height); +} + +// Конструктор копирования камеры +Camera::Camera(const Camera& copy) +: projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity) +{ + // Если у оригинала не было изменений - перепишем матрицу вида-проекции + if (!requiredRecalcVP) + vp = copy.vp; +} + +// Оператор присваивания +Camera& Camera::operator=(const Camera& other) +{ + projection = other.projection; + requiredRecalcVP = other.requiredRecalcVP; + sensitivity = other.sensitivity; + + // Если у оригинала не было изменений - перепишем матрицу вида-проекции + if (!requiredRecalcVP) + vp = other.vp; + + return *this; +} + +// Деструктор +Camera::~Camera() +{ + +} + +// Возвращает ссылку на константную матрицу проекции +const glm::mat4& Camera::getProjection() +{ + return projection; +} + +// Возвращает ссылку на константную матрицу вида +const glm::mat4& Camera::getView() +{ + recalcMatrices(); + + return view; +} + +// Возвращает ссылку на константную матрицу вида +const glm::mat4& Camera::getVP() +{ + recalcMatrices(); + + return vp; +} + +// Устанавливает заданную матрицу перспективы +void Camera::setPerspective(float fovy, float aspect) +{ + projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR); + requiredRecalcVP = true; +} + +// Устанавливает заданную ортографическую матрицу +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; +} + +// Изменяет чувствительность мыши +void Camera::setSensitivity(float sens) +{ + sensitivity = sens; +} + +// Возвращает чувствительность мыши +const float& Camera::getSensitivity() const +{ + return sensitivity; +} + +// Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed +void Camera::recalcMatrices() +{ + if (changed || parent_changed) + { + glm::vec3 _position = position; + glm::quat _rotation = rotation; + if (parent) // Если есть родитель + { + glm::mat4 normalized_transform = parent->getTransformMatrix(); + for (int i = 0; i < 3; i++) + { + glm::vec3 axis = glm::vec3(normalized_transform[i]); + normalized_transform[i] = glm::vec4(glm::normalize(axis), normalized_transform[i].w); + } + glm::vec4 tmp = normalized_transform * glm::vec4(_position, 1.0f); + tmp /= tmp.w; + _position = glm::vec3(tmp); + _rotation = glm::quat_cast(normalized_transform) * _rotation; + } + glm::mat4 rotationMatrix = glm::mat4_cast(glm::conjugate(_rotation)); + glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), -_position); + view = rotationMatrix * translationMatrix; + requiredRecalcVP = true; + } + + Node::recalcMatrices(); + + if (requiredRecalcVP) + { + vp = projection * view; + requiredRecalcVP = false; // Изменения применены + } +} + +// Поворачивает камеру на dx и dy пикселей с учетом чувствительности +void Camera::rotate(const glm::vec2 &xyOffset) +{ + // xyOffset - сдвиги координат мыши, xyOffset.x означает поворот вокруг оси Y, а xyOffset.y - поворот вокруг оси X + + // Вращение вокруг оси Y + glm::quat qY = glm::angleAxis(-xyOffset.x * sensitivity, glm::vec3(0.0f, 1.0f, 0.0f)); + + // Вращение вокруг оси X + glm::quat qX = glm::angleAxis(xyOffset.y * sensitivity, glm::vec3(1.0f, 0.0f, 0.0f)); + + // Сначала применяем вращение вокруг Y, затем вокруг X + rotation = qY * rotation * qX; + + changed = true; + invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed +}