224 lines
5.1 KiB
C++
224 lines
5.1 KiB
C++
#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
|