360 lines
12 KiB
C++
360 lines
12 KiB
C++
#include "RenderManager.h"
|
|
#include <fstream>
|
|
#include "Texture.h"
|
|
|
|
RenderManager::RenderManager(SDL_Window *window)
|
|
{
|
|
_window = window;
|
|
// 创建OpenGL上下文
|
|
_ctx = SDL_GL_CreateContext(window);
|
|
if (!_ctx)
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GL context create failed: %s\n", SDL_GetError());
|
|
}
|
|
|
|
// 初始化GLAD
|
|
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress))
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "GLAD init failed!\n");
|
|
}
|
|
|
|
// 获取窗口大小
|
|
int width, height;
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
// 设置视口
|
|
glViewport(0, 0, width, height);
|
|
// 设置窗口尺寸
|
|
_windowWidth = width;
|
|
_windowHeight = height;
|
|
// 设置正交投影矩阵
|
|
_orthoMatrix = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f);
|
|
|
|
// 设置清屏颜色
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
// 启用混合模式
|
|
glEnable(GL_BLEND);
|
|
// 默认设置标准alpha混合
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
// 初始化着色器程序
|
|
InitShaderProgram();
|
|
// 初始化绘制2D纹理缓冲程序
|
|
Init2DTextureProgram();
|
|
|
|
// 设定默认使用的着色器程序和缓冲程序
|
|
SetCurrentShaderProgram("normal");
|
|
SetCurrentBufferObject("2DTexture");
|
|
}
|
|
|
|
RenderManager::~RenderManager()
|
|
{
|
|
// 释放着色器程序
|
|
for (auto &[name, program] : _shaderProgramMap)
|
|
{
|
|
glDeleteProgram(program);
|
|
}
|
|
// 释放缓冲对象
|
|
for (auto &[name, program] : _RenderParamsMap)
|
|
{
|
|
glDeleteVertexArrays(1, &program.VAO);
|
|
glDeleteBuffers(1, &program.VBO);
|
|
glDeleteBuffers(1, &program.EBO);
|
|
}
|
|
// 释放OpenGL上下文
|
|
SDL_GL_DeleteContext(_ctx);
|
|
}
|
|
|
|
void RenderManager::InitShaderProgram()
|
|
{
|
|
std::ifstream shaderFile("shader/config.json");
|
|
|
|
nlohmann::json shaderConfig;
|
|
shaderFile >> shaderConfig;
|
|
|
|
for (auto &[groupName, groupData] : shaderConfig["shader"].items())
|
|
{
|
|
if (!groupData.is_object())
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "着色器组 %s 格式错误,跳过\n", groupName.c_str());
|
|
continue;
|
|
}
|
|
if (!groupData.contains("vertex") || !groupData["vertex"].is_string())
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 vertex 路径,跳过\n", groupName.c_str());
|
|
continue;
|
|
}
|
|
if (!groupData.contains("fragment") || !groupData["fragment"].is_string())
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 fragment 路径,跳过\n", groupName.c_str());
|
|
continue;
|
|
}
|
|
std::string vertPath = groupData["vertex"].get<std::string>();
|
|
std::string fragPath = groupData["fragment"].get<std::string>();
|
|
|
|
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertPath);
|
|
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragPath);
|
|
if (vertexShader == 0 || fragmentShader == 0)
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器编译失败,跳过\n", groupName.c_str());
|
|
continue;
|
|
}
|
|
|
|
GLuint program = glCreateProgram();
|
|
glAttachShader(program, vertexShader);
|
|
glAttachShader(program, fragmentShader);
|
|
glLinkProgram(program);
|
|
GLint programLinksuccess;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &programLinksuccess);
|
|
if (!programLinksuccess)
|
|
{
|
|
char infoLog[512];
|
|
glGetProgramInfoLog(program, 512, nullptr, infoLog);
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器链接失败,失败原因: %s\n", groupName.c_str(), infoLog);
|
|
}
|
|
|
|
_shaderProgramMap[groupName] = program;
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(fragmentShader);
|
|
}
|
|
}
|
|
|
|
void RenderManager::Init2DTextureProgram()
|
|
{
|
|
// 初始化顶点数据(矩形的四个顶点)
|
|
float vertices[] = {
|
|
0.0f, 0.0f, 0.0f, 0.0f, // 左下(单位坐标)
|
|
1.0f, 0.0f, 1.0f, 0.0f, // 右下
|
|
1.0f, 1.0f, 1.0f, 1.0f, // 右上
|
|
0.0f, 1.0f, 0.0f, 1.0f // 左上
|
|
};
|
|
// 索引数组
|
|
unsigned int indices[] = {0, 1, 2, 2, 3, 0};
|
|
GLuint VAO, VBO, EBO;
|
|
// 创建VAO/VBO/EBO
|
|
glGenVertexArrays(1, &VAO);
|
|
glGenBuffers(1, &VBO);
|
|
glGenBuffers(1, &EBO);
|
|
|
|
glBindVertexArray(VAO);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
|
|
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
|
|
glEnableVertexAttribArray(1);
|
|
// 创建完解绑
|
|
glBindVertexArray(0);
|
|
|
|
SetCurrentShaderProgram("normal");
|
|
glUseProgram(_currentShaderProgram);
|
|
|
|
// 获取uniform变量位置
|
|
GLuint _uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel");
|
|
GLuint _uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj");
|
|
GLuint _uTextureLoc = glGetUniformLocation(_currentShaderProgram, "uTexture");
|
|
if (_uModelLoc == -1 || _uViewProjLoc == -1 || _uTextureLoc == -1)
|
|
{
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "无法获取着色器 uniform 变量位置\n");
|
|
}
|
|
std::vector<GLuint> uniformLocs = {_uModelLoc, _uViewProjLoc, _uTextureLoc};
|
|
|
|
DrawLogicFunc drawFunc = [this](
|
|
RefPtr<Texture> textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata)
|
|
{
|
|
GLuint texture = textureObj->getID();
|
|
if (!texture || !dstrect)
|
|
return;
|
|
|
|
glUseProgram(_currentShaderProgram);
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
// 平移
|
|
model = glm::translate(model, glm::vec3(dstrect->x, dstrect->y, 0.0f));
|
|
|
|
// 计算旋转中心
|
|
float centerX = center ? center->x : dstrect->w * 0.5f;
|
|
float centerY = center ? center->y : dstrect->h * 0.5f;
|
|
|
|
// 旋转中心平移
|
|
model = glm::translate(model, glm::vec3(centerX, centerY, 0.0f));
|
|
|
|
// 旋转
|
|
if (angle != 0.0)
|
|
{
|
|
model = glm::rotate(model, glm::radians(static_cast<float>(angle)), glm::vec3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
// 旋转中心平移
|
|
model = glm::translate(model, glm::vec3(-centerX, -centerY, 0.0f));
|
|
|
|
// 缩放和翻转
|
|
float scaleX = dstrect->w;
|
|
float scaleY = dstrect->h;
|
|
|
|
// 应用翻转(通过负缩放实现)
|
|
if (flip & SDL_FLIP_HORIZONTAL)
|
|
scaleX = -scaleX;
|
|
if (flip & SDL_FLIP_VERTICAL)
|
|
scaleY = -scaleY;
|
|
|
|
model = glm::scale(model, glm::vec3(scaleX, scaleY, 1.0f));
|
|
|
|
// 翻转后的位置补偿
|
|
if (flip & SDL_FLIP_HORIZONTAL)
|
|
{
|
|
model = glm::translate(model, glm::vec3(-1.0f, 0.0f, 0.0f));
|
|
}
|
|
if (flip & SDL_FLIP_VERTICAL)
|
|
{
|
|
model = glm::translate(model, glm::vec3(0.0f, -1.0f, 0.0f));
|
|
}
|
|
|
|
// 设置着色器 uniforms
|
|
glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(model));
|
|
glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_orthoMatrix));
|
|
|
|
// 绑定纹理并设置采样器
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glUniform1i(_currentRenderParams.UnimLocs[2], 0);
|
|
|
|
// 处理纹理裁剪
|
|
int texWidth = textureObj->getSize().width;
|
|
int texHeight = textureObj->getSize().height;
|
|
|
|
// 计算纹理坐标
|
|
float minu, minv, maxu, maxv;
|
|
if (srcrect)
|
|
{
|
|
minu = (float)srcrect->x / texWidth;
|
|
minv = (float)srcrect->y / texHeight;
|
|
maxu = (float)(srcrect->x + srcrect->w) / texWidth;
|
|
maxv = (float)(srcrect->y + srcrect->h) / texHeight;
|
|
}
|
|
else
|
|
{
|
|
minu = 0.0f;
|
|
minv = 0.0f;
|
|
maxu = 1.0f;
|
|
maxv = 1.0f;
|
|
}
|
|
|
|
// 计算顶点位置(单位矩形,通过模型变换进行缩放和平移)
|
|
float minx = 0.0f;
|
|
float miny = 0.0f;
|
|
float maxx = 1.0f;
|
|
float maxy = 1.0f;
|
|
|
|
// 创建顶点数据(位置 + 纹理坐标)
|
|
float vertices[] = {
|
|
// 位置 // 纹理坐标
|
|
minx, miny, minu, minv, // 左上
|
|
maxx, miny, maxu, minv, // 右上
|
|
maxx, maxy, maxu, maxv, // 右下
|
|
minx, maxy, minu, maxv // 左下
|
|
};
|
|
|
|
// 更新VBO数据
|
|
glBindBuffer(GL_ARRAY_BUFFER, _currentRenderParams.VBO);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
|
|
|
// 绘制纹理
|
|
glBindVertexArray(_currentRenderParams.VAO);
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
|
glBindVertexArray(0);
|
|
};
|
|
|
|
GL_RenderParams bufferObject = {VAO, VBO, EBO, uniformLocs, drawFunc};
|
|
AddBufferObject("2DTexture", bufferObject);
|
|
}
|
|
|
|
void RenderManager::SetCurrentShaderProgram(std::string name)
|
|
{
|
|
if (_shaderProgramMap.find(name) != _shaderProgramMap.end())
|
|
{
|
|
this->_currentShaderProgram = _shaderProgramMap[name];
|
|
}
|
|
else
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到着色器程序: %s\n", name.c_str());
|
|
}
|
|
|
|
void RenderManager::SetCurrentBufferObject(std::string name)
|
|
{
|
|
if (_RenderParamsMap.find(name) != _RenderParamsMap.end())
|
|
{
|
|
this->_currentRenderParams = _RenderParamsMap[name];
|
|
}
|
|
else
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染参数: %s\n", name.c_str());
|
|
}
|
|
|
|
void RenderManager::ClearScreen()
|
|
{
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
void RenderManager::SwapBuffer()
|
|
{
|
|
SDL_GL_SwapWindow(_window);
|
|
}
|
|
|
|
void RenderManager::SetClipRect(const SDL_Rect *rect)
|
|
{
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(rect->x, _windowHeight - rect->y - rect->h, rect->w, rect->h);
|
|
}
|
|
|
|
void RenderManager::CloseClipRect()
|
|
{
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
void RenderManager::DrawTexture(RefPtr<Texture> textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata)
|
|
{
|
|
if (_currentRenderParams.DrawFunc != nullptr)
|
|
_currentRenderParams.DrawFunc(textureObj, srcrect, dstrect, angle, center, flip, userdata);
|
|
else
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染逻辑函数\n");
|
|
}
|
|
|
|
GLuint RenderManager::CompileShader(GLenum type, std::string Path)
|
|
{
|
|
std::ifstream Source("shader/" + Path);
|
|
if (!Source.is_open())
|
|
{
|
|
SDL_LogError(0, "Failed to open shader file: %s\n", Path.c_str());
|
|
return 0;
|
|
}
|
|
|
|
std::string ShaderSource((std::istreambuf_iterator<char>(Source)), std::istreambuf_iterator<char>());
|
|
const char *SourcePtr = ShaderSource.c_str();
|
|
|
|
GLuint ShaderId = glCreateShader(type);
|
|
glShaderSource(ShaderId, 1, &SourcePtr, NULL);
|
|
glCompileShader(ShaderId);
|
|
|
|
// 检查编译错误
|
|
GLint success;
|
|
glGetShaderiv(ShaderId, GL_COMPILE_STATUS, &success);
|
|
if (!success)
|
|
{
|
|
char infoLog[512];
|
|
glGetShaderInfoLog(ShaderId, 512, nullptr, infoLog);
|
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Shader compile error: %s\n", infoLog);
|
|
glDeleteShader(ShaderId);
|
|
return 0;
|
|
}
|
|
return ShaderId;
|
|
}
|
|
|
|
void RenderManager::AddBufferObject(std::string name, GL_RenderParams bufferObject)
|
|
{
|
|
_RenderParamsMap[name] = bufferObject;
|
|
}
|