Extra2D/Extra2D/include/extra2d/render/camera.h

224 lines
5.1 KiB
C
Raw Normal View History

#pragma once
#include <extra2d/core/math_types.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/mat4x4.hpp>
namespace extra2d {
/**
* @brief 2D
*
*
*/
class Camera {
public:
Camera() = default;
~Camera() = default;
/**
* @brief
*/
void setViewport(int x, int y, int width, int height) {
viewportX_ = x;
viewportY_ = y;
viewportWidth_ = width;
viewportHeight_ = height;
dirty_ = true;
}
/**
* @brief
*/
void setPosition(const Vec2& pos) {
position_ = pos;
dirty_ = true;
}
/**
* @brief
*/
void setZoom(float zoom) {
zoom_ = zoom;
dirty_ = true;
}
/**
* @brief
*/
void setRotation(float degrees) {
rotation_ = degrees;
dirty_ = true;
}
/**
* @brief
*/
void move(const Vec2& delta) {
position_ += delta;
dirty_ = true;
}
/**
* @brief
*/
void zoom(float factor) {
zoom_ *= factor;
dirty_ = true;
}
/**
* @brief
*/
void rotate(float degrees) {
rotation_ += degrees;
dirty_ = true;
}
/**
* @brief
*/
const Vec2& position() const { return position_; }
/**
* @brief
*/
float zoom() const { return zoom_; }
/**
* @brief
*/
float rotation() const { return rotation_; }
/**
* @brief
*/
const glm::mat4& viewMatrix() const {
if (dirty_) {
updateMatrices();
}
return viewMatrix_;
}
/**
* @brief
*/
const glm::mat4& projectionMatrix() const {
if (dirty_) {
updateMatrices();
}
return projectionMatrix_;
}
/**
* @brief
*/
const glm::mat4& viewProjectionMatrix() const {
if (dirty_) {
updateMatrices();
}
return viewProjectionMatrix_;
}
/**
* @brief 便
*/
const glm::mat4& getViewProjectionMatrix() {
if (dirty_) {
updateMatrices();
}
return viewProjectionMatrix_;
}
/**
* @brief
*/
Vec2 screenToWorld(const Vec2& screenPos) const {
if (dirty_) {
updateMatrices();
}
glm::vec4 ndc(
(screenPos.x - viewportX_) / viewportWidth_ * 2.0f - 1.0f,
1.0f - (screenPos.y - viewportY_) / viewportHeight_ * 2.0f,
0.0f, 1.0f
);
glm::mat4 invVP = glm::inverse(viewProjectionMatrix_);
glm::vec4 world = invVP * ndc;
return Vec2(world.x, world.y);
}
/**
* @brief
*/
Vec2 worldToScreen(const Vec2& worldPos) const {
if (dirty_) {
updateMatrices();
}
glm::vec4 clip = viewProjectionMatrix_ * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f);
glm::vec3 ndc(clip.x / clip.w, clip.y / clip.w, clip.z / clip.w);
return Vec2(
(ndc.x + 1.0f) * 0.5f * viewportWidth_ + viewportX_,
(1.0f - ndc.y) * 0.5f * viewportHeight_ + viewportY_
);
}
/**
* @brief
*/
int viewportWidth() const { return viewportWidth_; }
/**
* @brief
*/
int viewportHeight() const { return viewportHeight_; }
private:
void updateMatrices() const {
glm::vec3 eye(position_.x, position_.y, 0.0f);
glm::vec3 center(position_.x, position_.y, -1.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
viewMatrix_ = glm::lookAt(eye, center, up);
if (rotation_ != 0.0f) {
glm::mat4 rot = glm::rotate(glm::mat4(1.0f),
rotation_ * 3.14159265f / 180.0f,
glm::vec3(0.0f, 0.0f, 1.0f));
viewMatrix_ = rot * viewMatrix_;
}
if (zoom_ != 1.0f) {
viewMatrix_ = glm::scale(glm::mat4(1.0f),
glm::vec3(zoom_, zoom_, 1.0f)) * viewMatrix_;
}
float halfW = viewportWidth_ * 0.5f;
float halfH = viewportHeight_ * 0.5f;
projectionMatrix_ = glm::ortho(-halfW, halfW, -halfH, halfH, -1.0f, 1.0f);
viewProjectionMatrix_ = projectionMatrix_ * viewMatrix_;
dirty_ = false;
}
Vec2 position_;
float zoom_ = 1.0f;
float rotation_ = 0.0f;
int viewportX_ = 0;
int viewportY_ = 0;
int viewportWidth_ = 800;
int viewportHeight_ = 600;
mutable glm::mat4 viewMatrix_{1.0f};
mutable glm::mat4 projectionMatrix_{1.0f};
mutable glm::mat4 viewProjectionMatrix_{1.0f};
mutable bool dirty_ = true;
};
} // namespace extra2d