refactor: renderer (#29)

* refactor(render): move render files to subdirectory, add RAII vertex buffer/array classes

* refactor(render): encapsulate OpenGL textures into Texture class

* refactor(texture): encapsulate textures with unique_ptr and bind methods

Replace raw GLuint framebuffer textures with std::unique_ptr<Texture>. Update TextureManager to return const Texture* instead of GLuint. Use Texture::bind() for active texture binding. Add RGBA16F and RGB formats. Add texture parameter methods. Update destructors accordingly.

* refactor(render): use Texture, VertexBuffer, VertexArray classes

* feat(render): add FrameBuffer class to encapsulate framebuffer operations

* refactor(render): extract world rendering into WorldRenderer class

* refactor(render): rename projection and model matrix members for clarity

* chore(render): remove unused matrix members

* fix(texture-manager): correct delet_texture typo and add null checks for textures
This commit is contained in:
zhenyan121
2026-07-10 14:51:43 +08:00
committed by GitHub
parent b1341d7da4
commit 913809a5f0
33 changed files with 2263 additions and 1547 deletions

298
src/render/renderer.cpp Normal file
View File

@@ -0,0 +1,298 @@
#include "Cubed/render/renderer.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/dev_panel.hpp"
#include "Cubed/gameplay/client_player.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/primitive_data.hpp"
#include "Cubed/render/renderer_constants.hpp"
#include "Cubed/texture_manager.hpp"
#include "Cubed/tools/font.hpp"
#include "Cubed/tools/log.hpp"
#include "Cubed/tools/shader_tools.hpp"
#include <GLFW/glfw3.h>
#include <format>
#include <glm/gtc/type_ptr.hpp>
namespace Cubed {
Renderer::Renderer(const Camera& camera, ClientWorld& world,
const TextureManager& texture_manager, DevPanel& dev_panel)
: m_camera(camera), m_dev_panel(dev_panel),
m_texture_manager(texture_manager), m_world(world),
m_world_renderer(*this) {}
Renderer::~Renderer() {
if (m_init) {
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_outline_vbo.reset();
m_outline_indices_vbo.reset();
m_sky_vbo.reset();
m_ui_vbo.reset();
m_player_vbo.reset();
glBindVertexArray(0);
m_vao.clear();
}
}
void Renderer::hot_reload() {
auto& config = Config::get();
update_fov(config.get<double>("player.fov"));
}
void Renderer::init(bool debug_on) {
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
Logger::error("Failed to initialize glad");
exit(EXIT_FAILURE);
}
Logger::info("OpenGL Version: {}.{}", GLVersion.major, GLVersion.minor);
Logger::info("Renderer: {}",
reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
m_shaders.init();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifdef DEBUG_MODE
if (debug_on) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(
[](GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar* message,
const void*) {
Logger::log(Logger::Level::L_DEBUG,
std::source_location::current(), "GL Debug: {}",
reinterpret_cast<const char*>(message));
},
nullptr);
}
#endif
m_vao.resize(NUM_VAO);
VertexArray::unbind();
m_outline_vbo = std::make_unique<VertexBuffer>();
m_outline_indices_vbo =
std::make_unique<VertexBuffer>(BufferType::ELEMENT_ARRAY_BUFFER);
m_player_vbo = std::make_unique<VertexBuffer>();
m_quad_vbo = std::make_unique<VertexBuffer>();
m_sky_vbo = std::make_unique<VertexBuffer>();
m_ui_vbo = std::make_unique<VertexBuffer>();
m_vao[2].bind();
m_outline_vbo->buffer_data(CUBE_VER, sizeof(CUBE_VER));
m_vao[2].attribute(0, 3, GL_FLOAT, 0, 0);
m_outline_indices_vbo->buffer_data(OUTLINE_CUBE_INDICES,
sizeof(OUTLINE_CUBE_INDICES));
m_vao[1].bind();
m_sky_vbo->buffer_data(VERTICES_POS, sizeof(VERTICES_POS));
m_vao[1].attribute(0, 3, GL_FLOAT, 0, 0);
m_vao[3].bind();
for (int i = 0; i < 6; i++) {
Vertex2D vex{SQUARE_VERTICES[i][0], SQUARE_VERTICES[i][1],
SQUARE_TEXTURE_POS[i][0], SQUARE_TEXTURE_POS[i][1], 0};
m_ui.emplace_back(vex);
}
m_ui_vbo->buffer_data(m_ui.data(), m_ui.size() * sizeof(Vertex2D));
m_vao[3].attribute(0, 3, GL_FLOAT, sizeof(Vertex2D), (void*)0);
m_vao[3].attribute(1, 2, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, s));
m_vao[3].attribute(2, 1, GL_FLOAT, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, layer));
init_quad();
init_text();
hot_reload();
m_world_renderer.init();
VertexArray::unbind();
VertexBuffer::unbind();
m_init = true;
}
const Shader& Renderer::get_shader(const std::string& name) const {
return m_shaders.get_shader(name);
}
void Renderer::init_quad() {
m_vao[0].bind();
m_quad_vbo->buffer_data(QUAD_VERTICES, sizeof(QUAD_VERTICES));
m_vao[0].attribute(0, 2, GL_FLOAT, 4 * sizeof(float), (void*)0);
m_vao[0].attribute(1, 2, GL_FLOAT, 4 * sizeof(float),
(void*)(2 * sizeof(float)));
}
void Renderer::init_text() {
m_vao[4].bind();
DebugCollector::get().init_text();
}
void Renderer::render() {
glDisable(GL_FRAMEBUFFER_SRGB);
// clear screen
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_world_renderer.render();
render_ui();
render_text();
render_dev_panel();
}
void Renderer::render_text() {
const auto& shader = get_shader("text");
shader.use();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
shader.set_loc("projection", m_ui_proj_matrix);
auto& texts = DebugCollector::get().all_texts();
for (auto& t : texts) {
t.second.render(shader);
}
glEnable(GL_DEPTH_TEST);
}
void Renderer::render_ui() {
const auto& shader = get_shader("ui");
shader.use();
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader.set_loc("m_matrix", m_ui_model_matrix);
shader.set_loc("proj_matrix", m_ui_proj_matrix);
m_vao[3].bind();
m_texture_manager.get_ui_array()->bind(0);
glDrawArrays(GL_TRIANGLES, 0, 6);
Tools::check_opengl_error();
glEnable(GL_DEPTH_TEST);
}
void Renderer::update(float delta_time) { m_delta_time = delta_time; }
void Renderer::update_fov(float fov) {
m_fov = fov;
m_world_proj_matrix =
glm::perspective(glm::radians(fov), m_aspect, NEAR_PLANE, FAR_PLANE);
}
void Renderer::update_proj_matrix(float aspect, float width, float height) {
m_aspect = aspect;
m_world_proj_matrix =
glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE);
m_ui_proj_matrix = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
// scale and then translate
m_ui_model_matrix =
glm::translate(glm::mat4(1.0f),
glm::vec3(width / 2.0f, height / 2.0f, 0.0)) *
glm::scale(glm::mat4(1.0f), glm::vec3(50.0f, 50.0f, 1.0f));
}
void Renderer::updata_framebuffer(int width, int height) {
if (width <= 0 || height <= 0)
return;
m_world_renderer.updata_framebuffer(width, height);
FrameBuffer::unbind();
m_width = width;
m_height = height;
}
void Renderer::render_dev_panel() {
glDisable(GL_DEPTH_TEST);
m_dev_panel.render();
glEnable(GL_DEPTH_TEST);
}
float& Renderer::ambient_strength() {
return m_world_renderer.ambient_strength();
}
bool& Renderer::discard_transparent() {
return m_world_renderer.discard_transparent();
}
bool& Renderer::shader_on() { return m_world_renderer.shader_on(); }
bool& Renderer::water_perturb() { return m_world_renderer.water_perturb(); }
bool& Renderer::water_depth_fade() {
return m_world_renderer.water_depth_fade();
}
bool& Renderer::pbr() { return m_world_renderer.pbr(); }
bool& Renderer::flip_y() { return m_world_renderer.flip_y(); }
int& Renderer::shadow_mode() { return m_world_renderer.shadow_mode(); }
int& Renderer::light_cull_face() { return m_world_renderer.light_cull_face(); }
int& Renderer::light_size_uv() { return m_world_renderer.light_size_uv(); }
float& Renderer::min_radius() { return m_world_renderer.min_radius(); }
float& Renderer::max_radius() { return m_world_renderer.max_radius(); }
int& Renderer::samples() { return m_world_renderer.samples(); }
float& Renderer::specular_strength() {
return m_world_renderer.specular_strength();
}
float& Renderer::cloud_speed() { return m_world_renderer.cloud_speed(); }
float& Renderer::cloud_threshold_low() {
return m_world_renderer.cloud_threshold_low();
}
float& Renderer::cloud_threshold_high() {
return m_world_renderer.cloud_threshold_high();
}
float& Renderer::refract_strength() {
return m_world_renderer.refract_strength();
}
float& Renderer::underwater_fog_density() {
return m_world_renderer.underwater_fog_density();
}
float& Renderer::water_density() { return m_world_renderer.water_density(); }
const Camera& Renderer::camera() const { return m_camera; }
const ClientWorld& Renderer::world() const { return m_world; }
ClientWorld& Renderer::world() { return m_world; }
const glm::mat4& Renderer::world_proj_matrix() const {
return m_world_proj_matrix;
}
const TextureManager& Renderer::texture_mamger() const {
return m_texture_manager;
}
float Renderer::delta_time() const { return m_delta_time; }
float Renderer::height() const { return m_height; }
float Renderer::width() const { return m_width; }
const glm::mat4& Renderer::p_mat() const { return m_world_proj_matrix; }
const std::vector<VertexArray>& Renderer::vao() const { return m_vao; }
} // namespace Cubed