feat(renderer): Add ModelRender,Camera,TextRenderer

- Add ModelRender
- Add Camera
- Add TextRenderer
- Add RTTI Object
class
- Add PhysicsCollider interface: getClassName method
- Add AlignedMemoryStack class
- fix Logger and LoggerSpdLog
- fix Model2D
method: getPhysics, remove const qualifier
This commit is contained in:
lyxyz5223
2025-11-17 22:14:25 +08:00
parent e3b42e4f70
commit 242f225b75
17 changed files with 2371 additions and 72 deletions

View File

@@ -0,0 +1,2 @@
#include "pch.h"
#include "AlignedMemoryStack.h"

View File

@@ -0,0 +1,53 @@
#pragma once
#include <memory>
#include <vector>
#include <stack>
class AlignedMemoryStack {
char* buffer = nullptr;
size_t capacity = 0;
size_t offset = 0;
std::stack<size_t> markers;
public:
AlignedMemoryStack(size_t initialSize = 64 * 1024) {
buffer = new char[initialSize];
capacity = initialSize;
}
~AlignedMemoryStack() {
delete[] buffer;
}
void* pushStackAlloc(size_t size, size_t align = alignof(std::max_align_t)) {
size_t aligned = (offset + align - 1) & ~(align - 1);
if (aligned + size > capacity) {
// 简单扩容策略:直接翻倍
size_t newCapacity = (aligned + size) * 2;
char* newBuffer = new char[newCapacity];
std::memcpy(newBuffer, buffer, offset);
delete[] buffer;
buffer = newBuffer;
capacity = newCapacity;
}
void* ptr = buffer + aligned;
markers.push(offset);
offset = aligned + size;
return ptr;
}
template<typename PtrType>
PtrType* pushStackAllocate(size_t size, size_t align = alignof(std::max_align_t)) {
return pushStackAlloc(size, align);
}
void popStack() {
offset = markers.top();
markers.pop();
}
void reset() {
offset = 0;
markers = std::stack<size_t>();
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,516 @@
#pragma once
#include <Logger.h>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat3x3.hpp>
#include <glfw/glfw3.h>
#include <glm/glm.hpp>
#include "model/Mesh2D.h"
#include "systems/sources/ShaderProgram.h"
#include "systems/Camera.h"
#include "TextRenderer.h"
#include "systems/sources/CompleteShader.h"
#include "systems/sources/ShaderManagement.h"
#include "model/util/LightSource.h"
#include "model/Model2D.h"
#include "model/util/PhysicsSystem.h"
#include "systems/buffer/Tesselator.h"
#include "systems/buffer/BufferBuilder.h"
class IllegalStateException : public std::exception {
private:
std::string message;
public:
IllegalStateException(const std::string& msg) {
this->message = msg;
}
char const* what() const noexcept override {
return message.c_str();
}
const std::string& getMessage() const {
return message;
}
operator std::string() const {
return message;
}
};
// 单例模式
class VIVID_2D_MYDLL_API ModelRender {
private:
explicit ModelRender() = default; // 默认构造函数
ModelRender(const ModelRender& other) = delete; // 复制构造函数
ModelRender(const ModelRender&& other) = delete; // 移动构造函数
~ModelRender() = default; // 默认析构函数
public:
// 获取全局唯一实例
static ModelRender* instance() {
if (s_instance == nullptr) {
s_instance = new ModelRender();
}
return s_instance;
}
static void destroyInstance() {
if (s_instance) {
ModelRender* tmp = s_instance;
s_instance = nullptr;
delete tmp;
}
}
// 类型定义
typedef glm::vec2 Vector2f;
typedef glm::vec3 Vector3f;
typedef glm::vec4 Vector4f;
typedef glm::mat3 Matrix3f;
typedef Vivid2D::Mesh2D Mesh2D;
typedef Vivid2D::Model2D Model2D;
typedef Vivid2D::util::LightSource LightSource;
typedef Vivid2D::util::PhysicsSystem PhysicsSystem;
typedef Vivid2D::util::PhysicsParticle PhysicsParticle;
typedef Vivid2D::util::CircleCollider CircleCollider;
typedef Vivid2D::util::RectangleCollider RectangleCollider;
typedef Vivid2D::ModelPart ModelPart;
typedef Vivid2D::Render::Buffer::Tesselator Tesselator;
typedef Vivid2D::Render::Buffer::BufferBuilder BufferBuilder;
using ByteBuffer = std::vector<unsigned char>;
/**
* 缩略图边界计算结果
*/
struct ThumbnailBounds {
GLfloat minX, maxX, minY, maxY;
GLfloat scale;
GLfloat offsetX, offsetY;
};
struct MeshGLResources {
GLuint vao = 0;
GLuint vbo = 0;
GLuint ebo = 0;
void dispose() {
if (ebo != 0) {
glDeleteBuffers(1, &ebo);
ebo = 0;
}
if (vbo != 0) {
glDeleteBuffers(1, &vbo);
vbo = 0;
}
if (vao != 0) {
glDeleteVertexArrays(1, &vao);
vao = 0;
}
}
};
public:
// 常量
static constexpr double c_pi = 3.14159265358979323846;
private:
Logger logger{ "ModelRender" };
// 全局唯一实例
static ModelRender* s_instance;
bool m_isInitialized = false;
struct Size {
int width;
int height;
};
/**
* 视口大小(宽高)(像素),定义渲染区域的大小
* 默认值:(800, 600) (像素)
*
* @see #setViewport(int, int)
* @see #setViewport(Size)
*/
Size m_viewportSize{ 800, 600 };
/**
* 清除颜色RGBA用于在每帧开始时清空颜色缓冲区
* 默认值:黑色不透明 (0.0f, 0.0f, 0.0f, 1.0f)
*
* @see RenderSystem#clearColor(GLfloat, GLfloat, GLfloat, GLfloat)
*/
Vector4f m_clearColor{ 0.0f, 0.0f, 0.0f, 1.0f };
/**
* 深度测试启用标志,控制是否进行深度缓冲测试
* 在2D渲染中通常禁用以提高性能
* 默认值false禁用
*
* @see RenderSystem#enableDepthTest()
* @see RenderSystem#disableDepthTest()
*/
bool m_enableDepthTest = false;
/**
* 混合功能启用标志,控制透明度和颜色混合
* 默认值true启用
*
* @see RenderSystem#enableBlend()
* @see RenderSystem#disableBlend()
*/
bool m_enableBlending = true;
/**
* 最大光源数量,用于限制同时启用的光源数量
* 默认值80
*/
int m_maxLightCount = 80;
// ================== 着色器与资源管理 ==================
/**
* 默认着色器程序,用于大多数模型的渲染
* 包含基础的光照、纹理和变换功能
*
* @see #compileDefaultShader()
*/
ShaderProgram* m_defaultProgram = nullptr;
/**
* 网格GPU资源缓存管理已上传到GPU的网格数据
* 键Mesh2D对象
* 值对应的OpenGL资源VAO、VBO、EBO
*
* @see MeshGLResources
*/
std::unordered_map<Mesh2D, MeshGLResources> m_meshResources;
/**
* 纹理单元分配器,用于管理多个纹理的绑定
* 确保不同的纹理绑定到正确的纹理单元
* 默认从0开始递增分配
*/
[[deprecated]]int m_textureUnitAllocator;
/**
* 默认白色纹理ID当模型没有指定纹理时使用
* 这是一个1x1的纯白色纹理确保模型有基本的颜色显示
*
* @see #createDefaultTexture()
*/
int m_defaultTextureId = 0;
// ================== 碰撞箱渲染配置 ==================
/**
* 碰撞箱渲染开关,控制是否在场景中显示物理碰撞体的轮廓
* 调试时非常有用,可以直观看到碰撞边界
* 默认值true启用
*/
bool m_renderColliders = true;
/**
* 碰撞箱线框宽度,控制碰撞体轮廓线的粗细
* 单位:像素
* 默认值1.0f
*/
GLfloat m_colliderLineWidth = 1.0f;
/**
* 碰撞箱颜色RGBA定义碰撞体轮廓的显示颜色
* 默认值:白色不透明 (1.0f, 1.0f, 1.0f, 1.0f)
*/
Vector4f m_colliderColor{ 1.0f, 1.0f, 1.0f, 1.0f };
/**
* 圆形碰撞体细分数量,控制圆形碰撞体的平滑度
* 值越高圆形越平滑,但渲染开销也越大
* 默认值32在性能和视觉效果间取得平衡
*/
int m_circleSegments = 32;
/**
* 光源位置渲染开关,控制是否在场景中显示光源的位置
* 用点状标记显示每个启用的光源位置
* 默认值true启用
*/
bool m_renderLightPositions = true;
// ================== 摄像机状态 ==================
/**
* 默认摄像机,用于控制场景的视图和缩放
* 默认位置:(0, 0)
*/
Camera m_camera{0, 0};
// ================== 字体管理 ==================
std::unique_ptr<TextRenderer> m_defaultTextRenderer;
Size m_fontBitmapSize{ 512, 512 };
int m_fontFirstChar = 32;
int m_fontCharCount = 96;
public:
// ================== 摄像机API方法 ==================
/**
* 获取摄像机对象副本
*/
Camera getCamera() const {
return m_camera;
}
/**
* 获取当前实例摄像机引用
*/
Camera& getCameraReference() {
return m_camera;
}
/**
* 设置摄像机位置
*/
void setCameraPosition(GLfloat x, GLfloat y) {
m_camera.setPosition(x, y);
}
/**
* 设置摄像机缩放
*/
void setCameraZoom(GLfloat zoom) {
m_camera.setZoom(zoom);
}
/**
* 设置摄像机Z轴位置
*/
void setCameraZPosition(GLfloat z) {
m_camera.setZPosition(z);
}
/**
* 移动摄像机
*/
void moveCamera(GLfloat dx, GLfloat dy) {
m_camera.move(dx, dy);
}
/**
* 缩放摄像机
*/
void zoomCamera(GLfloat factor) {
m_camera.zoom(factor);
}
/**
* 重置摄像机
*/
void resetCamera() {
m_camera.reset();
}
/**
* 启用/禁用摄像机
*/
void setCameraEnabled(bool enabled) {
m_camera.setEnabled(enabled);
}
/**
* 构建考虑摄像机变换的投影矩阵
*/
Matrix3f buildCameraProjection(int width, int height);
// ================== 初始化 / 清理 ==================
void initialize() noexcept(false);
/**
* 初始化所有非默认着色器的基础信息(顶点坐标等)
*/
void initNonDefaultShaders();
/**
* 初始化着色器的基础uniforms顶点坐标相关
*/
void initShaderBasicUniforms(ShaderProgram* program, CompleteShader* shader);
auto& getTextRenderer() const {
return m_defaultTextRenderer;
}
// ================== 工具 ==================
static Matrix3f buildOrthoProjection(int width, int height);
/**
* 渲染文字
*
* @param text 文字内容
* @param x 世界坐标 X
* @param y 世界坐标 Y ,反转的
* @param color RGBA 颜色
*/
void renderText(std::string text, GLfloat x, GLfloat y, Vector4f color);
/**
* 获取默认摄像机与当前摄像机之间的偏移量
*
* @return Vector2f 偏移向量 (dx, dy)
*/
Vector2f getCameraOffset();
void setViewport(int width, int height);
// ================== 辅助:外部获取状态 ==================
bool isInitialized() const {
return m_isInitialized;
}
int getLoadedMeshCount() const {
return m_meshResources.size();
}
void cleanup();
// ================== 渲染流程 (已修改) ==================
void render(GLfloat deltaTime, Model2D& model);
// ================== 缩略图渲染方法 ==================
/**
* 渲染模型缩略图(图层式渲染,不受摄像机控制)
*
* <p>该方法提供类似PS图层预览的缩略图渲染功能</p>
* <ul>
* <li>固定位置和大小,不受摄像机影响</li>
* <li>自动缩放确保模型完全可见</li>
* <li>禁用复杂效果以提高性能</li>
* <li>独立的渲染状态管理</li>
* </ul>
*
* @param model 要渲染的模型
* @param x 缩略图左上角X坐标屏幕坐标
* @param y 缩略图左上角Y坐标屏幕坐标
* @param width 缩略图宽度
* @param height 缩略图高度
*/
void renderThumbnail(Model2D& model, GLfloat x, GLfloat y, GLfloat width, GLfloat height);
private:
void logGLInfo();
void uploadLightsToShader(ShaderProgram* sp, Model2D& model);
void setupGLState();
void compileDefaultShader();
void createDefaultTexture();
/**
* 计算模型的边界和合适的缩放比例
*/
ThumbnailBounds calculateThumbnailBounds(Model2D& model, GLfloat thumbWidth, GLfloat thumbHeight);
/**
* 递归计算模型的边界
*/
void calculateModelBounds(Model2D& model, ThumbnailBounds bounds, Matrix3f parentTransform);
/**
* 递归计算部件及其子部件的边界
*/
void calculateModelBoundsForPart(ModelPart* part, ThumbnailBounds bounds, Matrix3f parentTransform);
/**
* 计算单个部件的边界
*/
void calculatePartBounds(ModelPart* part, ThumbnailBounds bounds, Matrix3f worldTransform);
/**
* 构建缩略图专用的正交投影矩阵
*/
Matrix3f buildThumbnailProjection(GLfloat width, GLfloat height);
/**
* 缩略图专用的部件渲染
*/
void renderPartForThumbnail(ModelPart* part, Matrix3f parentTransform);
/**
* 缩略图专用的网格渲染
*/
void renderMeshForThumbnail(Mesh2D* mesh, Matrix3f modelMatrix);
/**
* 设置缩略图专用的简化光照
*/
void setupThumbnailLighting(ShaderProgram* sp, Model2D& model);
/**
* 设置所有非默认着色器的顶点坐标相关uniform
*/
void setupNonDefaultShaders(Matrix3f projection, Matrix3f view);
void renderLightPositions(Model2D& model);
/**
* 绘制简洁的灯泡形状
*
* @param position 灯泡位置
* @param intensity 光源强度,用于控制灯泡大小
*/
void drawLightBulb(Vector2f position, GLfloat intensity);
/**
* 绘制十字标记(用于环境光)
*/
void drawCrossMark(Vector2f position, GLfloat size);
/**
* 关键修改点:在渲染前确保更新 part 的 worldTransform
* 然后直接使用 part.getWorldTransform() 作为 uModelMatrix 传入 shader。
*/
void renderPartRecursive(ModelPart* part, Matrix3f parentMat);
void renderMesh(Mesh2D* mesh, Matrix3f modelMatrix);
// ================== 渲染碰撞箱相关实现 ==================
void renderPhysicsColliders(PhysicsSystem* physics);
/**
* 绘制圆形碰撞框(线框)
* 使用临时 VAO/VBO每帧创建并删除简单实现
*/
void drawCircleColliderWire(Vector2f center, GLfloat radius);
/**
* 绘制矩形碰撞框(线框)
*/
void drawRectangleColliderWire(Vector2f center, GLfloat width, GLfloat height);
// ================== uniform 设置辅助(内部使用,确保 program 已绑定) ==================
void setUniformIntInternal(ShaderProgram* sp, std::string name, int value);
void setUniformVec3Internal(ShaderProgram* sp, std::string name, Vector3f vec);
void setUniformVec2Internal(ShaderProgram* sp, std::string name, Vector2f vec);
void setUniformFloatInternal(ShaderProgram* sp, std::string name, GLfloat value);
void setUniformVec4Internal(ShaderProgram* sp, std::string name, Vector4f vec);
void setUniformMatrix3(ShaderProgram* sp, std::string name, Matrix3f m);
// ================== 部件属性 ==================
void setPartUniforms(ShaderProgram* sp, ModelPart* part);
};

11
Vivid2DRenderer/Object.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <string>
class Object {
public:
virtual ~Object() = default;
// RTTI
virtual std::string getClassName() const = 0;
};

View File

@@ -0,0 +1,318 @@
#include "pch.h"
#include "TextRenderer.h"
#include <systems/sources/ShaderProgram.h>
#include "systems/RenderSystem.h"
#include <systems/sources/ShaderManagement.h>
#include <systems/buffer/Tesselator.h>
#include "AlignedMemoryStack.h"
typedef Vivid2D::Render::Buffer::Tesselator Tesselator;
typedef Vivid2D::Render::Buffer::BufferBuilder BufferBuilder;
Logger TextRenderer::logger{ "TextRenderer" };
int TextRenderer::CHINESE_FIRST_CHAR = 0x4E00; // CJK Unified Ideographs 常用汉字起始范围
int TextRenderer::CHINESE_CHAR_COUNT = 20000;
TextRenderer::TextRenderer(int bitmapWidth, int bitmapHeight, int firstChar, int charCount)
: bitmapWidth(bitmapWidth),
bitmapHeight(bitmapHeight),
firstChar(firstChar),
charCount(charCount)
{
}
/**
* 初始化字体渲染器
*/
void TextRenderer::initialize(ByteBuffer fontData, float fontHeight)
{
if (initialized) {
logger.warning("TextRenderer already initialized");
return;
}
if (fontData.empty() || fontHeight <= 0) {
logger.error("Invalid font data or font height");
return;
}
ShaderProgram* shader = ShaderManagement::getShaderProgram("TextShader");
if (!shader) {
logger.error("TextShader not found");
return;
}
shader->use();
try {
asciiCharData.resize(charCount);
ByteBuffer asciiBitmap(bitmapWidth * bitmapHeight);
int asciiRes = stbtt_BakeFontBitmap(
fontData.data(),
0, fontHeight,
asciiBitmap.data(),
bitmapWidth, bitmapHeight, firstChar, charCount,
asciiCharData.data());
if (asciiRes <= 0) {
logger.error("ASCII font bake failed, result: {}", asciiRes);
cleanup();
return;
}
asciiTextureId = createTextureFromBitmap(bitmapWidth, bitmapHeight, asciiBitmap);
if (asciiTextureId == 0) {
logger.error("Failed to create ASCII texture");
cleanup();
return;
}
// 烘焙中文 - 使用更大的纹理和正确的字符范围
int chineseTexSize = 4096; // 中文字符需要更大的纹理
// 分配足够的空间来存储 CHINESE_CHAR_COUNT 个字符的数据
chineseCharData.resize(CHINESE_CHAR_COUNT);
ByteBuffer chineseBitmap(chineseTexSize * chineseTexSize);
// 关键:烘焙从 CHINESE_FIRST_CHAR 开始的 CHINESE_CHAR_COUNT 个连续字符
int chineseRes = stbtt_BakeFontBitmap(fontData.data(),
0, fontHeight,
chineseBitmap.data(),
chineseTexSize, chineseTexSize, CHINESE_FIRST_CHAR, CHINESE_CHAR_COUNT,
chineseCharData.data());
if (chineseRes <= 0) {
logger.error("Chinese font bake failed, result: {}", chineseRes);
cleanup();
return;
}
chineseTextureId = createTextureFromBitmap(chineseTexSize, chineseTexSize, chineseBitmap);
if (chineseTextureId == 0) {
logger.error("Failed to create Chinese texture");
cleanup();
return;
}
initialized = true;
logger.debug("TextRenderer initialized, ASCII tex={}, Chinese tex={}", asciiTextureId, chineseTextureId);
}
catch (std::exception e) {
logger.error("Exception during TextRenderer init: {}", e.what(), e);
cleanup();
}
catch (...) {
logger.error("Unknown exception during TextRenderer init");
cleanup();
}
shader->stop();
}
/**
* 获取一行文字的宽度(带缩放)
*/
float TextRenderer::getTextWidth(std::string text, float scale) const
{
if (!initialized || text.empty()) return 0.0f;
float width = 0.0f;
for (int i = 0; i < text.length(); i++) {
char c = text[i];
if (c >= firstChar && c < firstChar + charCount) {
BakedChar bakedChar = asciiCharData[c - firstChar];
width += bakedChar.xadvance * scale;
}
else {
// 修复中文索引逻辑:检查字符是否在烘焙的连续范围内
if (c >= CHINESE_FIRST_CHAR && c < CHINESE_FIRST_CHAR + CHINESE_CHAR_COUNT) {
int idx = c - CHINESE_FIRST_CHAR; // 关键:使用 Unicode 差值作为索引
BakedChar bakedChar = chineseCharData[idx];
width += bakedChar.xadvance * scale;
}
else {
// 对于未找到的字符,使用空格宽度
width += 0.5f * scale; // 估计值
}
}
}
return width;
}
void TextRenderer::renderText(std::string text, float x, float y, Vector4f color, float scale)
{
if (!initialized || text.empty()) return;
if (scale <= 0.0f) scale = 1.0f;
RenderSystem::assertOnRenderThread();
RenderSystem::pushState();
try {
ShaderProgram* shader = ShaderManagement::getShaderProgram("TextShader");
if (!shader) {
logger.error("TextShader not found");
return;
}
shader->use();
ShaderManagement::setUniformVec4(shader, "uColor", color);
ShaderManagement::setUniformInt(shader, "uTexture", 0);
RenderSystem::enableBlend();
RenderSystem::blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
RenderSystem::disableDepthTest();
thread_local AlignedMemoryStack stack;
try {
auto* q = stack.pushStackAllocate<AlignedQuad>(sizeof(AlignedQuad));
float xpos = x;
float ypos = y;
Tesselator& t = Tesselator::getInstance();
BufferBuilder& builder = t.getBuilder();
// 按字符类型分组渲染以减少纹理切换
int currentTexture = -1;
bool batchStarted = false;
for (int i = 0; i < text.length(); i++) {
char c = text[i];
int targetTexture;
BakedCharBuffer charBuffer;
int texWidth, texHeight;
if (c >= firstChar && c < firstChar + charCount) {
targetTexture = asciiTextureId;
charBuffer = asciiCharData;
texWidth = bitmapWidth;
texHeight = bitmapHeight;
stbtt_GetBakedQuad(charBuffer.data(), texWidth, texHeight, c - firstChar, &xpos, &ypos, q, true);
}
else {
// 修复中文索引逻辑:检查字符是否在烘焙的连续范围内
if (c >= CHINESE_FIRST_CHAR && c < CHINESE_FIRST_CHAR + CHINESE_CHAR_COUNT) {
targetTexture = chineseTextureId;
charBuffer = chineseCharData;
texWidth = 4096;
texHeight = 4096;
// 关键修复:索引是字符的 Unicode 减去起始 Unicode
int idx = c - CHINESE_FIRST_CHAR;
stbtt_GetBakedQuad(charBuffer.data(), texWidth, texHeight, idx, &xpos, &ypos, q, true);
}
else {
continue; // 跳过不支持的字符
}
}
// 如果纹理改变,结束当前批次
if (targetTexture != currentTexture) {
if (batchStarted) {
t.end();
batchStarted = false;
}
RenderSystem::bindTexture(targetTexture);
currentTexture = targetTexture;
}
// 开始新批次(如果需要)
if (!batchStarted) {
builder.begin(GL_TRIANGLES, (text.length() - i) * 6);
batchStarted = true;
}
// 应用缩放并计算顶点
float sx0 = x + (q->x0 - x) * scale;
float sx1 = x + (q->x1 - x) * scale;
float sy0 = y + (q->y0 - y) * scale;
float sy1 = y + (q->y1 - y) * scale;
builder.vertex(sx0, sy0, q->s0, q->t0);
builder.vertex(sx1, sy0, q->s1, q->t0);
builder.vertex(sx0, sy1, q->s0, q->t1);
builder.vertex(sx1, sy0, q->s1, q->t0);
builder.vertex(sx1, sy1, q->s1, q->t1);
builder.vertex(sx0, sy1, q->s0, q->t1);
}
// 结束最后一个批次
if (batchStarted) {
t.end();
}
stack.popStack();
}
catch (...) {
stack.popStack();
throw;
}
}
catch (std::exception e) {
logger.error("Error rendering text: {}", e.what(), e);
}
catch (...) {
logger.error("Unknown error rendering text");
}
RenderSystem::popState();
}
int TextRenderer::createTextureFromBitmap(int width, int height, ByteBuffer pixels)
{
RenderSystem::assertOnRenderThread();
try {
int textureId = RenderSystem::genTextures();
RenderSystem::bindTexture(textureId);
RenderSystem::pixelStore(GL_UNPACK_ALIGNMENT, 1);
RenderSystem::texImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0,
GL_RED, GL_UNSIGNED_BYTE, pixels.data());
RenderSystem::setTextureMinFilter(GL_LINEAR);
RenderSystem::setTextureMagFilter(GL_LINEAR);
RenderSystem::setTextureWrapS(GL_CLAMP_TO_EDGE);
RenderSystem::setTextureWrapT(GL_CLAMP_TO_EDGE);
// 设置纹理swizzle以便单通道纹理在着色器中显示为白色
RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
RenderSystem::pixelStore(GL_UNPACK_ALIGNMENT, 4);
RenderSystem::bindTexture(0);
return textureId;
}
catch (std::exception e) {
logger.error("Failed to create texture from bitmap: {}", e.what(), e);
return 0;
}
catch (...) {
logger.error("Unknown error creating texture from bitmap");
return 0;
}
}
void TextRenderer::cleanup()
{
RenderSystem::assertOnRenderThread();
if (asciiTextureId != 0)
{
RenderSystem::deleteTextures(asciiTextureId);
asciiTextureId = 0;
}
if (chineseTextureId != 0)
{
RenderSystem::deleteTextures(chineseTextureId);
chineseTextureId = 0;
}
if (!asciiCharData.empty())
{
asciiCharData.clear();
asciiCharData.shrink_to_fit();
}
if (!chineseCharData.empty())
{
chineseCharData.clear();
chineseCharData.shrink_to_fit();
}
initialized = false;
logger.debug("TextRenderer cleaned up");
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <Logger.h>
#include <string_view>
#include <glm/vec4.hpp>
#include <stb_truetype.h>
namespace glm {
struct vec4;
}
class TextRenderer
{
private:
using Vector4f = glm::vec4;
using BakedChar = stbtt_bakedchar;
using BakedCharBuffer = std::vector<BakedChar>;
using ByteBuffer = std::vector<unsigned char>;
using AlignedQuad = stbtt_aligned_quad;
static Logger logger;
int bitmapWidth = 512;
int bitmapHeight = 512;
int firstChar = 32;
int charCount = 96;
BakedCharBuffer asciiCharData;
BakedCharBuffer chineseCharData;
int asciiTextureId = 0;
int chineseTextureId = 0;
bool initialized = false;
// 中文字符起始编码(选择一个不冲突的范围)
static int CHINESE_FIRST_CHAR; // CJK Unified Ideographs 常用汉字起始范围
static int CHINESE_CHAR_COUNT;
public:
TextRenderer(int bitmapWidth, int bitmapHeight, int firstChar, int charCount);
void initialize(ByteBuffer, float);
bool isInitialized() const;
float getTextWidth(std::string text, float scale) const;
/**
* 获取一行文字的宽度(单位:像素)
*/
float getTextWidth(std::string text) const {
return getTextWidth(text, 1.0f);
}
/**
* 渲染文字
*/
void renderText(std::string text, float x, float y, Vector4f color, float scale);
void renderText(std::string text, float x, float y, Vector4f color) {
renderText(text, x, y, color, 1.0f);
}
int createTextureFromBitmap(int width, int height, ByteBuffer pixels);
void cleanup();
bool isInitialized() const {
return initialized;
}
int getAsciiTextureId() const {
return asciiTextureId;
}
int getChineseTextureId() const {
return chineseTextureId;
}
};

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@@ -70,6 +70,13 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>libs\logger\include;$(ProjectDir);$(IncludePath)</IncludePath>
<LibraryPath>libs\logger\build\Debugx64;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnableManifest>true</VcpkgEnableManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@@ -112,11 +119,13 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>Logger_d.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -209,6 +218,8 @@ endlocal
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AlignedMemoryStack.h" />
<ClInclude Include="ModelRender.h" />
<ClInclude Include="model\AnimationParameter.h" />
<ClInclude Include="model\FrameInterpolator.h" />
<ClInclude Include="model\util\AnimationClip.h" />
@@ -221,10 +232,12 @@ endlocal
<ClInclude Include="model\util\ModelPose.h" />
<ClInclude Include="model\util\PhysicsSystem.h" />
<ClInclude Include="model\util\VertexList.h" />
<ClInclude Include="Object.h" />
<ClInclude Include="systems\buffer\BufferUploader.h" />
<ClInclude Include="systems\buffer\BufferBuilder.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="systems\Camera.h" />
<ClInclude Include="systems\MultiSelectionBoxRenderer.h" />
<ClInclude Include="systems\sources\def\Shader2D.h" />
<ClInclude Include="systems\sources\def\SolidColorShader.h" />
@@ -237,8 +250,11 @@ endlocal
<ClInclude Include="systems\buffer\Tesselator.h" />
<ClInclude Include="model\util\Vertex.h" />
<ClInclude Include="systems\Texture.h" />
<ClInclude Include="TextRenderer.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AlignedMemoryStack.cpp" />
<ClCompile Include="ModelRender.cpp" />
<ClCompile Include="model\AnimationParameter.cpp" />
<ClCompile Include="model\FrameInterpolator.cpp" />
<ClCompile Include="model\util\AnimationClip.cpp" />
@@ -260,6 +276,7 @@ endlocal
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="systems\Camera.cpp" />
<ClCompile Include="systems\MultiSelectionBoxRenderer.cpp" />
<ClCompile Include="systems\sources\def\Shader2D.cpp" />
<ClCompile Include="systems\sources\def\SolidColorShader.cpp" />
@@ -270,6 +287,7 @@ endlocal
<ClCompile Include="systems\buffer\Tesselator.cpp" />
<ClCompile Include="model\util\Vertex.cpp" />
<ClCompile Include="systems\Texture.cpp" />
<ClCompile Include="TextRenderer.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />

View File

@@ -13,6 +13,27 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="源文件\systems">
<UniqueIdentifier>{aace2215-85f9-46d7-aa76-35c982fe4c00}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\systems\buffer">
<UniqueIdentifier>{780d2429-421a-438d-9602-8e7b1d7e9975}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\systems\sources">
<UniqueIdentifier>{1fb8ab3b-c4ca-4647-ad21-9ae610ea6d30}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\systems\sources\def">
<UniqueIdentifier>{f052c1ce-f35a-4901-8922-116e4991b2a6}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\model">
<UniqueIdentifier>{8fa89cd9-6cd6-411a-80d2-4a27e482f1c0}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\model\util">
<UniqueIdentifier>{fba61944-faac-4a9d-a29c-fccc95a13587}</UniqueIdentifier>
</Filter>
<Filter Include="头文件\systems">
<UniqueIdentifier>{555ebff3-f682-47fe-9316-2cc820ab476b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
@@ -21,9 +42,6 @@
<ClInclude Include="pch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="systems\RenderSystem.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="systems\sources\ShaderProgram.h">
<Filter>头文件</Filter>
</ClInclude>
@@ -60,18 +78,12 @@
<ClInclude Include="model\util\VertexList.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="systems\Texture.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="model\util\BoundingBox.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="model\Mesh2D.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="systems\MultiSelectionBoxRenderer.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="model\ModelPart.h">
<Filter>头文件</Filter>
</ClInclude>
@@ -99,6 +111,30 @@
<ClInclude Include="model\FrameInterpolator.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="ModelRender.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="systems\Camera.h">
<Filter>头文件\systems</Filter>
</ClInclude>
<ClInclude Include="systems\Texture.h">
<Filter>头文件\systems</Filter>
</ClInclude>
<ClInclude Include="systems\MultiSelectionBoxRenderer.h">
<Filter>头文件\systems</Filter>
</ClInclude>
<ClInclude Include="systems\RenderSystem.h">
<Filter>头文件\systems</Filter>
</ClInclude>
<ClInclude Include="TextRenderer.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="Object.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="AlignedMemoryStack.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@@ -107,76 +143,88 @@
<ClCompile Include="pch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="ModelRender.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\RenderSystem.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\sources\ShaderProgram.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\sources\ShaderManagement.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\TextShader.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\SolidColorShader.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\Shader2D.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\Tesselator.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\BufferBuilder.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\BufferUploader.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\Vertex.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\VertexList.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="systems\Texture.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\BoundingBox.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\Mesh2D.cpp">
<Filter>源文件</Filter>
<Filter>源文件\systems</Filter>
</ClCompile>
<ClCompile Include="systems\MultiSelectionBoxRenderer.cpp">
<Filter>源文件</Filter>
<Filter>源文件\systems</Filter>
</ClCompile>
<ClCompile Include="model\ModelPart.cpp">
<Filter>源文件</Filter>
<ClCompile Include="systems\Texture.cpp">
<Filter>源文件\systems</Filter>
</ClCompile>
<ClCompile Include="systems\Camera.cpp">
<Filter>源文件\systems</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\BufferBuilder.cpp">
<Filter>源文件\systems\buffer</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\BufferUploader.cpp">
<Filter>源文件\systems\buffer</Filter>
</ClCompile>
<ClCompile Include="systems\buffer\Tesselator.cpp">
<Filter>源文件\systems\buffer</Filter>
</ClCompile>
<ClCompile Include="systems\sources\ShaderProgram.cpp">
<Filter>源文件\systems\sources</Filter>
</ClCompile>
<ClCompile Include="systems\sources\ShaderManagement.cpp">
<Filter>源文件\systems\sources</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\Shader2D.cpp">
<Filter>源文件\systems\sources\def</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\SolidColorShader.cpp">
<Filter>源文件\systems\sources\def</Filter>
</ClCompile>
<ClCompile Include="systems\sources\def\TextShader.cpp">
<Filter>源文件\systems\sources\def</Filter>
</ClCompile>
<ClCompile Include="model\AnimationParameter.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\LightSource.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\ModelPose.cpp">
<Filter>源文件</Filter>
<Filter>源文件\model</Filter>
</ClCompile>
<ClCompile Include="model\Model2D.cpp">
<Filter>源文件</Filter>
<Filter>源文件\model</Filter>
</ClCompile>
<ClCompile Include="model\util\PhysicsSystem.cpp">
<Filter>源文件</Filter>
<ClCompile Include="model\Mesh2D.cpp">
<Filter>源文件\model</Filter>
</ClCompile>
<ClCompile Include="model\util\AnimationLayer.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="model\util\AnimationClip.cpp">
<Filter>源文件</Filter>
<ClCompile Include="model\ModelPart.cpp">
<Filter>源文件\model</Filter>
</ClCompile>
<ClCompile Include="model\FrameInterpolator.cpp">
<Filter>源文件\model</Filter>
</ClCompile>
<ClCompile Include="model\util\AnimationClip.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\AnimationLayer.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\BoundingBox.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\LightSource.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\ModelPose.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\PhysicsSystem.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\Vertex.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="model\util\VertexList.cpp">
<Filter>源文件\model\util</Filter>
</ClCompile>
<ClCompile Include="TextRenderer.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="AlignedMemoryStack.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>

View File

@@ -168,7 +168,7 @@ public:
virtual ~LoggerInterface() = default;
virtual void setLevel(LogLevel level) noexcept(false) = 0;
virtual void setGlobalLevel(LogLevel level) noexcept(false) = 0;
virtual void level() const noexcept = 0;
virtual LogLevel level() const noexcept = 0;
virtual LoggerStream trace() noexcept = 0;
virtual LoggerStream debug() noexcept = 0;
virtual LoggerStream info() noexcept = 0;
@@ -275,6 +275,7 @@ public:
~Logger();
void setGlobalLevel(LogLevel level) noexcept(false) override;
void setLevel(LogLevel level) noexcept(false) override;
LogLevel level() const noexcept override;
// 流式日志打印
LoggerStream trace() noexcept override;
LoggerStream debug() noexcept override;
@@ -288,6 +289,41 @@ public:
void warning(const std::string &msg) noexcept override;
void error(const std::string &msg) noexcept override;
void critical(const std::string &msg) noexcept override;
// fmt + args
template <typename... Args>
void trace(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::trace, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void debug(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::debug, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void info(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::info, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warning(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::warning, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::error, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void critical(const std::string& fmt, Args &&...args) noexcept {
_loggerHelper(&LoggerInterface::critical, fmt, std::forward<Args>(args)...);
}
private:
template <typename... Args>
void _loggerHelper(void (LoggerInterface::* func)(const std::string&), const std::string& fmt, Args &&...args) noexcept {
std::string formattedMsg = LoggerFormatNS::format(fmt, std::forward<Args>(args)...);
// 处理后交给具体的日志实现
(this->*func)(formattedMsg);
}
};

View File

@@ -52,7 +52,36 @@ public:
}
return spdLevel;
}
static LogLevel spdLogLevelToLogLevel(SpdLogLevel level) {
LogLevel l = LogLevel::trace;
switch (level)
{
case SpdLogUtils::SpdLogLevel::trace:
l = LogLevel::trace;
break;
case SpdLogUtils::SpdLogLevel::debug:
l = LogLevel::debug;
break;
case SpdLogUtils::SpdLogLevel::info:
l = LogLevel::info;
break;
case SpdLogUtils::SpdLogLevel::warning:
l = LogLevel::warning;
break;
case SpdLogUtils::SpdLogLevel::error:
l = LogLevel::error;
break;
case SpdLogUtils::SpdLogLevel::critical:
l = LogLevel::critical;
break;
case SpdLogUtils::SpdLogLevel::off:
l = LogLevel::off;
break;
default:
break;
}
return l;
}
typedef spdlog::color_mode SpdLogColorMode;
static SpdLogColorMode logColorModeToSpdLogColorMode(LogColorMode colorMode) {
SpdLogColorMode cm = SpdLogColorMode::automatic;
@@ -268,6 +297,11 @@ void Logger::setLevel(LogLevel level) noexcept(false)
}
}
LogLevel Logger::level() const noexcept
{
//level::level_enum logger::level()
return SpdLogUtils::spdLogLevelToLogLevel(static_cast<SpdLogUtils::SpdLogLevel>(pImpl->logger->level()));
}
// 带参数日志打印
void Logger::trace(const std::string &msg) noexcept

View File

@@ -382,7 +382,7 @@ namespace Vivid2D {
* @brief 获取模型的物理系统。
* @return 指向物理系统的常量指针。
*/
const util::PhysicsSystem* getPhysics() const { return m_physics.get(); }
util::PhysicsSystem* getPhysics() { return m_physics.get(); }
/**
* @brief 获取模型当前应用的姿态。

View File

@@ -10,6 +10,8 @@
#include <unordered_map>
#include <memory>
#include <chrono>
#include <Object.h>
namespace Vivid2D {
class Model2D;
@@ -299,7 +301,7 @@ namespace Vivid2D::util {
/**
* 物理碰撞体接口
*/
struct VIVID_2D_MYDLL_API PhysicsCollider {
struct VIVID_2D_MYDLL_API PhysicsCollider : public Object {
virtual ~PhysicsCollider() = default;
virtual bool collidesWith(const PhysicsParticle& particle) const = 0;
virtual void resolveCollision(PhysicsParticle& particle, float deltaTime) = 0;
@@ -313,6 +315,7 @@ namespace Vivid2D::util {
*/
class VIVID_2D_MYDLL_API CircleCollider : public PhysicsCollider {
public:
std::string getClassName() const override { return "CircleCollider"; }
CircleCollider(std::string id, glm::vec2 center, float radius);
bool collidesWith(const PhysicsParticle& particle) const override;
void resolveCollision(PhysicsParticle& particle, float deltaTime) override;
@@ -337,6 +340,7 @@ namespace Vivid2D::util {
*/
class VIVID_2D_MYDLL_API RectangleCollider : public PhysicsCollider {
public:
std::string getClassName() const override { return "RectangleCollider"; }
RectangleCollider(std::string id, glm::vec2 center, float width, float height);
bool collidesWith(const PhysicsParticle& particle) const override;
void resolveCollision(PhysicsParticle& particle, float deltaTime) override;

View File

@@ -0,0 +1,94 @@
#include "Camera.h"
#include <glm/vec2.hpp>
#include <algorithm>
Camera::Camera()
: m_pos(new Vector2f(0.0f, 0.0f))
{
}
Camera::Camera(float x, float y)
: m_pos(new Vector2f(x, y))
{
}
Camera::Camera(const Camera& other)
: m_pos(new Vector2f(other.m_pos->x, other.m_pos->y))
, m_zoom(other.m_zoom)
, m_zPos(other.m_zPos)
, m_enabled(other.m_enabled)
{
}
Camera::~Camera()
{
}
void Camera::setPosition(float x, float y)
{
*m_pos = Vector2f(x, y);
}
void Camera::setPosition(Vector2f pos)
{
*m_pos = pos;
}
Camera::Vector2f Camera::getPosition() const
{
return *m_pos;
}
void Camera::setZoom(float zoom)
{
this->m_zoom = std::max(0.1f, std::min(10.0f, zoom));
}
float Camera::getZoom() const
{
return m_zoom;
}
void Camera::setZPosition(float z)
{
this->m_zPos = z;
}
float Camera::getZPosition() const
{
return m_zPos;
}
void Camera::setEnabled(bool enabled)
{
this->m_enabled = enabled;
}
/**
* 获取摄像机是否启用
*/
bool Camera::isEnabled() const
{
return m_enabled;
}
void Camera::move(float dx, float dy)
{
*m_pos += Vector2f(dx, dy);
}
void Camera::zoom(float factor)
{
m_zoom *= factor;
m_zoom = std::max(0.1f, std::min(10.0f, m_zoom));
}
void Camera::reset() {
*m_pos = Vector2f(0.0f, 0.0f);
m_zoom = 1.0f;
m_zPos = 0.0f;
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <memory>
namespace glm {
struct vec2;
}
class Camera {
public:
using Vector2f = glm::vec2;
private:
std::unique_ptr<Vector2f> m_pos;
float m_zoom = 1.0f;
float m_zPos = 0.0f;
bool m_enabled = true;
public:
explicit Camera();
explicit Camera(float x, float y);
Camera(const Camera& other);
~Camera();
void setPosition(float x, float y);
void setPosition(Vector2f);
Vector2f getPosition() const;
void setZoom(float);
void zoom(float);
float getZoom() const;
void setZPosition(float);
float getZPosition() const;
void move(float x, float y);
void reset();
bool isEnabled() const;
void setEnabled(bool);
};