From 94fed9a810528a31b3460f479d929d8d1fb7649c Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 24 Jul 2026 18:35:09 +0800 Subject: [PATCH] feat(render): add model loading and rendering pipeline --- assets/model/creature/pig.glb | Bin 0 -> 15992 bytes assets/shaders/model_frag.glsl | 15 +++ assets/shaders/model_vert.glsl | 22 +++++ include/Cubed/app.hpp | 3 +- include/Cubed/render/model_manager.hpp | 21 ++++ include/Cubed/render/model_node.hpp | 42 ++++++++ include/Cubed/render/model_renderer.hpp | 22 +++++ include/Cubed/render/renderer.hpp | 13 ++- include/Cubed/render/texture.hpp | 4 +- include/Cubed/tools/model_loader.hpp | 22 +++++ include/Cubed/tools/shader_tools.hpp | 2 +- src/CMakeLists.txt | 3 + src/app.cpp | 4 +- src/render/model_manager.cpp | 32 ++++++ src/render/model_renderer.cpp | 48 +++++++++ src/render/renderer.cpp | 14 ++- src/render/shader_manager.cpp | 2 + src/scene/world_scene.cpp | 1 + src/tools/model_loader.cpp | 124 ++++++++++++++++++++++++ src/tools/shader_tools.cpp | 10 +- 20 files changed, 393 insertions(+), 11 deletions(-) create mode 100644 assets/model/creature/pig.glb create mode 100644 assets/shaders/model_frag.glsl create mode 100644 assets/shaders/model_vert.glsl create mode 100644 include/Cubed/render/model_manager.hpp create mode 100644 include/Cubed/render/model_node.hpp create mode 100644 include/Cubed/render/model_renderer.hpp create mode 100644 include/Cubed/tools/model_loader.hpp create mode 100644 src/render/model_manager.cpp create mode 100644 src/render/model_renderer.cpp create mode 100644 src/tools/model_loader.cpp diff --git a/assets/model/creature/pig.glb b/assets/model/creature/pig.glb new file mode 100644 index 0000000000000000000000000000000000000000..b94f8d9f5cdbde09adc2e071fea37ff1ea4e7c95 GIT binary patch literal 15992 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 literal 0 HcmV?d00001 diff --git a/assets/shaders/model_frag.glsl b/assets/shaders/model_frag.glsl new file mode 100644 index 0000000..1fe8f33 --- /dev/null +++ b/assets/shaders/model_frag.glsl @@ -0,0 +1,15 @@ +#version 460 + +in vec2 tc; +in vec3 normal; +in vec3 vert_pos; + +out vec4 color; + +layout (binding = 0) uniform sampler2D samp; + + +void main() { + color = texture(samp, tc); + +} diff --git a/assets/shaders/model_vert.glsl b/assets/shaders/model_vert.glsl new file mode 100644 index 0000000..7681fb8 --- /dev/null +++ b/assets/shaders/model_vert.glsl @@ -0,0 +1,22 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in vec3 aNormal; + +uniform mat4 mv_matrix; +uniform mat4 proj_matrix; + +out vec3 normal; +out vec2 tc; +out vec3 vert_pos; + + +void main() { + //vec4 worldPos = modelMatrix * vec4(pos, 1.0); + vec4 viewPos = mv_matrix * vec4(pos, 1.0); + tc = texCoord; + vert_pos = pos; + //normal = normalize(mat3(norm_matrix) * aNormal); + gl_Position = proj_matrix * viewPos; +} \ No newline at end of file diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index 4d9ee45..2d509aa 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -3,6 +3,7 @@ #include "Cubed/audio/audio_engine.hpp" #include "Cubed/config.hpp" #include "Cubed/dev_panel.hpp" +#include "Cubed/render/model_manager.hpp" #include "Cubed/render/renderer.hpp" #include "Cubed/scene/scene_manager.hpp" #include "Cubed/texture_manager.hpp" @@ -49,7 +50,7 @@ private: Window m_window; TextureManager m_texture_manager; - + ModelManager m_model_manager; AudioEngine m_audio; Renderer m_renderer; diff --git a/include/Cubed/render/model_manager.hpp b/include/Cubed/render/model_manager.hpp new file mode 100644 index 0000000..18af757 --- /dev/null +++ b/include/Cubed/render/model_manager.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "Cubed/render/model_node.hpp" +#include "Cubed/tools/model_loader.hpp" +namespace Cubed { +class ModelManager { +public: + ModelManager(); + ModelManager(const ModelManager&) = delete; + ModelManager(ModelManager&&) = delete; + ModelManager& operator=(const ModelManager&) = delete; + ModelManager& operator=(ModelManager&&) = delete; + ~ModelManager(); + const ModelNode& get_model(const std::string& model_name); + void init(); + +private: + ModelLoader m_loader; + std::unordered_map m_models; + const ModelNode& load_model(const std::string& model_name); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/model_node.hpp b/include/Cubed/render/model_node.hpp new file mode 100644 index 0000000..b3e7414 --- /dev/null +++ b/include/Cubed/render/model_node.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Cubed/primitive_data.hpp" +#include "Cubed/render/texture.hpp" +#include "Cubed/render/vertex_array.hpp" +#include "Cubed/render/vertex_buffer.hpp" + +#include +#include +#include +#include +namespace Cubed { + +struct Mesh { + std::vector vertices; + std::vector indices; + std::unique_ptr vbo; + std::unique_ptr ebo; + std::unique_ptr vao; + std::unique_ptr texture; + void upload() { + vao = std::make_unique(); + vao->bind(); + vbo = std::make_unique(); + vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D)); + ebo = std::make_unique(BufferType::ELEMENT_ARRAY_BUFFER); + ebo->buffer_data(indices.data(), indices.size() * sizeof(uint32_t)); + vao->attribute(0, 3, GL_FLOAT, sizeof(Vertex3D), (void*)0); + vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, s)); + vao->attribute(2, 3, GL_FLOAT, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, nx)); + }; +}; +struct ModelNode { + std::string name; + glm::mat4 transform{1.0f}; + std::vector meshes; + std::vector children; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/model_renderer.hpp b/include/Cubed/render/model_renderer.hpp new file mode 100644 index 0000000..64df2a4 --- /dev/null +++ b/include/Cubed/render/model_renderer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "Cubed/render/model_node.hpp" +#include "Cubed/shader.hpp" + +#include +namespace Cubed { +class Renderer; +class Camera; +class ModelRender { +public: + ModelRender(Renderer& renderer); + void render_model(const std::string& name, const glm::vec3& pos, + Camera& camera); + +private: + Renderer& m_renderer; + void render_node(const ModelNode& node, const glm::mat4& parent, + const glm::mat4& view, const Shader& shader); + void render_mesh(const Mesh& mesh); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/renderer.hpp b/include/Cubed/render/renderer.hpp index 00edc28..15a57d1 100644 --- a/include/Cubed/render/renderer.hpp +++ b/include/Cubed/render/renderer.hpp @@ -4,6 +4,7 @@ #include "Cubed/constants.hpp" #include "Cubed/input/event.hpp" #include "Cubed/primitive_data.hpp" +#include "Cubed/render/model_renderer.hpp" #include "Cubed/render/player_renderer.hpp" #include "Cubed/render/shader_manager.hpp" #include "Cubed/render/vertex_array.hpp" @@ -19,12 +20,14 @@ namespace Cubed { class TextureManager; class ClientWorld; +class ModelManager; class DevPanel; class Renderer { public: constexpr static int NUM_VAO = 7; - Renderer(TextureManager& texture_manager, Config& config); + Renderer(TextureManager& texture_manager, Config& config, + ModelManager& model_manager); ~Renderer(); void reload_config(); void init(); @@ -79,9 +82,14 @@ public: bool handle_event(const Event& e); + void render_model(const std::string& name, const glm::vec3& pos, + Camera& camera); + + ModelManager& model_manager(); + private: TextureManager& m_texture_manager; - + ModelManager& m_model_manager; bool m_init = false; float m_aspect = 0.0f; @@ -118,6 +126,7 @@ private: std::vector m_ui; WorldRenderer m_world_renderer; + ModelRender m_model_renderer; Config& m_config; bool handle_window_resize_event(const WindowResizeEvent& e); diff --git a/include/Cubed/render/texture.hpp b/include/Cubed/render/texture.hpp index f058ef5..129a400 100644 --- a/include/Cubed/render/texture.hpp +++ b/include/Cubed/render/texture.hpp @@ -38,9 +38,11 @@ enum TextureFormat : GLenum { R8 = GL_R8, RGB = GL_RGB, RGBA8 = GL_RGBA8, + BGRA = GL_BGRA }; - +// You need to set the texture scaling method, otherwise it will render as +// black. class Texture { public: explicit Texture(TextureType type); diff --git a/include/Cubed/tools/model_loader.hpp b/include/Cubed/tools/model_loader.hpp new file mode 100644 index 0000000..da301da --- /dev/null +++ b/include/Cubed/tools/model_loader.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "Cubed/render/model_node.hpp" + +#include +#include +namespace Cubed { + +class ModelLoader { +public: + ModelLoader(); + ModelNode load(const std::string& path); + +private: + Assimp::Importer m_importer; + ModelNode process_node(aiNode* node, const aiScene* scene); + Mesh process_mesh(aiMesh* mesh, const aiScene* scene); + bool process_texture(Mesh& mesh, aiMaterial* material, const aiScene* scene, + aiTextureType type); + glm::mat4 convert_matrix(const aiMatrix4x4& matrix); +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/shader_tools.hpp b/include/Cubed/tools/shader_tools.hpp index 443328a..d9eb81e 100644 --- a/include/Cubed/tools/shader_tools.hpp +++ b/include/Cubed/tools/shader_tools.hpp @@ -47,7 +47,7 @@ bool check_opengl_error(); std::string read_shader_source(const std::string& file_path); ImageData load_image_data(const std::string& tex_image_path, - bool check_exist = true); + bool check_exist = true, bool full_path = false); } // namespace Tools diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f16a3f8..589c8be 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,4 +92,7 @@ target_sources(${PROJECT_NAME} ui/row_layout.cpp ui/item_slot.cpp ui/inventory_ui.cpp + tools/model_loader.cpp + render/model_manager.cpp + render/model_renderer.cpp ) \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index 1abc51a..2ae7ddc 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -20,7 +20,8 @@ App::App() : m_game_config(ASSETS_PATH "config.toml"), m_window(m_game_config), m_texture_manager(m_game_config), m_audio(m_game_config), - m_renderer(m_texture_manager, m_game_config), m_scene_manager(*this) {} + m_renderer(m_texture_manager, m_game_config, m_model_manager), + m_scene_manager(*this) {} App::~App() { stop_text_input(); @@ -85,6 +86,7 @@ void App::init(int argc, char** argv) { Logger::info("Renderer Init Success"); // MapTable::init_map(); m_texture_manager.init_texture(); + m_model_manager.init(); Logger::info("Texture Load Success"); m_scene_manager.request_push(SceneType::MAIN_MENU); diff --git a/src/render/model_manager.cpp b/src/render/model_manager.cpp new file mode 100644 index 0000000..fc568fa --- /dev/null +++ b/src/render/model_manager.cpp @@ -0,0 +1,32 @@ +#include "Cubed/render/model_manager.hpp" + +#include "Cubed/tools/log.hpp" +namespace Cubed { + +ModelManager::ModelManager() {} + +ModelManager::~ModelManager() {} + +void ModelManager::init() { + +}; + +const ModelNode& ModelManager::get_model(const std::string& model_name) { + auto it = m_models.find(model_name); + if (it == m_models.end()) { + return load_model(model_name); + } + return it->second; +} + +const ModelNode& ModelManager::load_model(const std::string& model_name) { + std::string path = ASSETS_PATH + model_name; + auto model = m_loader.load(path); + auto [it, instert] = m_models.try_emplace(model_name, std::move(model)); + if (!instert) { + Logger::error("Model Key {} already exist!", model_name); + } + return it->second; +} + +} // namespace Cubed \ No newline at end of file diff --git a/src/render/model_renderer.cpp b/src/render/model_renderer.cpp new file mode 100644 index 0000000..c6b2ba3 --- /dev/null +++ b/src/render/model_renderer.cpp @@ -0,0 +1,48 @@ +#include "Cubed/render/model_renderer.hpp" + +#include "Cubed/camera.hpp" +#include "Cubed/render/model_manager.hpp" +#include "Cubed/render/renderer.hpp" +namespace Cubed { +ModelRender::ModelRender(Renderer& renderer) : m_renderer(renderer) {} + +void ModelRender::render_model(const std::string& name, const glm::vec3& pos, + Camera& camera) { + auto& model_manager = m_renderer.model_manager(); + auto& root = model_manager.get_model(name); + glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos); + auto& shader = m_renderer.get_shader("model_shader"); + glm::mat4 view = camera.get_camera_lookat(); + shader.use(); + shader.set_loc("proj_matrix", m_renderer.p_mat()); + render_node(root, transform, view, shader); +} + +void ModelRender::render_node(const ModelNode& node, const glm::mat4& parent, + const glm::mat4& view, const Shader& shader) { + + glm::mat4 transform = parent * node.transform; + + shader.set_loc("mv_matrix", view * transform); + for (auto& mesh : node.meshes) { + render_mesh(mesh); + } + + for (auto& child : node.children) { + render_node(child, transform, view, shader); + } +} + +void ModelRender::render_mesh(const Mesh& mesh) { + mesh.vao->bind(); + if (mesh.texture) { + mesh.texture->bind(0); + } else { + Logger::error("Model Texture Not Find"); + } + + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), + GL_UNSIGNED_INT, 0); +} + +} // namespace Cubed \ No newline at end of file diff --git a/src/render/renderer.cpp b/src/render/renderer.cpp index d26c525..80ffe37 100644 --- a/src/render/renderer.cpp +++ b/src/render/renderer.cpp @@ -15,9 +15,10 @@ namespace Cubed { -Renderer::Renderer(TextureManager& texture_manager, Config& config) - : m_texture_manager(texture_manager), m_world_renderer(*this), - m_config(config) {} +Renderer::Renderer(TextureManager& texture_manager, Config& config, + ModelManager& model_manager) + : m_texture_manager(texture_manager), m_model_manager(model_manager), + m_world_renderer(*this), m_model_renderer(*this), m_config(config) {} Renderer::~Renderer() { if (m_init) { @@ -249,6 +250,12 @@ void Renderer::render_dev_panel(DevPanel& dev_panel) { glEnable(GL_DEPTH_TEST); } +void Renderer::render_model(const std::string& name, const glm::vec3& pos, + Camera& camera) { + glEnable(GL_DEPTH_TEST); + m_model_renderer.render_model(name, pos, camera); +} + bool Renderer::handle_event(const Event& e) { return std::visit(Overloaded{[](const MouseMoveEvent&) { return false; }, [](const MouseButtonEvent&) { return false; }, @@ -340,4 +347,5 @@ float Renderer::frame_height() const { return m_frame_height; } float Renderer::frame_width() const { return m_frame_width; } const glm::mat4& Renderer::p_mat() const { return m_world_proj_matrix; } const std::vector& Renderer::vao() const { return m_vao; } +ModelManager& Renderer::model_manager() { return m_model_manager; } } // namespace Cubed \ No newline at end of file diff --git a/src/render/shader_manager.cpp b/src/render/shader_manager.cpp index 3669b7e..96d66ac 100644 --- a/src/render/shader_manager.cpp +++ b/src/render/shader_manager.cpp @@ -35,6 +35,8 @@ void ShaderManager::init() { "shaders/depth_player_fragment_shader.glsl"); register_shader("rect", "shaders/rect_v_shader.glsl", "shaders/rect_f_shader.glsl"); + register_shader("model_shader", "shaders/model_vert.glsl", + "shaders/model_frag.glsl"); } void ShaderManager::register_shader(const std::string& name, diff --git a/src/scene/world_scene.cpp b/src/scene/world_scene.cpp index 827ab4b..33c15b5 100644 --- a/src/scene/world_scene.cpp +++ b/src/scene/world_scene.cpp @@ -65,6 +65,7 @@ void WorldScene::render(Renderer& renderer) { return; } renderer.render_world(m_client_world); + renderer.render_model("model/creature/pig.glb", {0, 100, 0}, m_camera); if (m_show_hud) { m_hud_ui.render(renderer); } diff --git a/src/tools/model_loader.cpp b/src/tools/model_loader.cpp new file mode 100644 index 0000000..cd86be3 --- /dev/null +++ b/src/tools/model_loader.cpp @@ -0,0 +1,124 @@ +#include "Cubed/tools/model_loader.hpp" + +#include "Cubed/tools/cubed_assert.hpp" +#include "Cubed/tools/log.hpp" +#include "Cubed/tools/shader_tools.hpp" + +#include +#include +namespace Cubed { +ModelLoader::ModelLoader() {} + +ModelNode ModelLoader::load(const std::string& path) { + const aiScene* scene = m_importer.ReadFile( + path, aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_FlipUVs | + aiProcess_JoinIdenticalVertices | + aiProcess_ImproveCacheLocality); + if (!scene || !scene->mRootNode) { + Logger::error("Load Model {} Error: {}", path, + m_importer.GetErrorString()); + return {}; + } + return process_node(scene->mRootNode, scene); +} +ModelNode ModelLoader::process_node(aiNode* node, const aiScene* scene) { + ModelNode result; + result.name = node->mName.C_Str(); + result.transform = convert_matrix(node->mTransformation); + + for (unsigned i = 0; i < node->mNumMeshes; ++i) { + aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; + result.meshes.emplace_back(process_mesh(mesh, scene)); + } + + for (unsigned i = 0; i < node->mNumChildren; ++i) { + result.children.emplace_back(process_node(node->mChildren[i], scene)); + } + return result; +} + +Mesh ModelLoader::process_mesh(aiMesh* mesh, const aiScene* scene) { + Mesh result; + for (unsigned i = 0; i < mesh->mNumVertices; ++i) { + Vertex3D v{}; + v.x = mesh->mVertices[i].x; + v.y = mesh->mVertices[i].y; + v.z = mesh->mVertices[i].z; + if (mesh->HasTextureCoords(0)) { + v.s = mesh->mTextureCoords[0][i].x; + v.t = mesh->mTextureCoords[0][i].y; + } + if (mesh->HasNormals()) { + v.nx = mesh->mNormals[i].x; + v.ny = mesh->mNormals[i].y; + v.nz = mesh->mNormals[i].z; + } else { + Logger::warn("Model Does not have Normals!"); + } + result.vertices.emplace_back(v); + } + + for (unsigned i = 0; i < mesh->mNumFaces; ++i) { + aiFace face = mesh->mFaces[i]; + + for (unsigned j = 0; j < face.mNumIndices; ++j) { + result.indices.emplace_back(face.mIndices[j]); + } + } + aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; + if (!process_texture(result, material, scene, aiTextureType_BASE_COLOR)) { + if (!process_texture(result, material, scene, aiTextureType_DIFFUSE)) { + if (!process_texture(result, material, scene, + aiTextureType_UNKNOWN)) { + Logger::error("Can't Load model texture"); + ASSERT(false); + } + } + } + result.upload(); + return result; +} + +bool ModelLoader::process_texture(Mesh& mesh, aiMaterial* material, + const aiScene* scene, aiTextureType type) { + aiString texture_path; + if (material->GetTexture(type, 0, &texture_path) != AI_SUCCESS) { + return false; + } + mesh.texture = std::make_unique(TextureType::TEXTURE_2D); + if (texture_path.C_Str()[0] == '*') { + int index = std::stoi(texture_path.C_Str() + 1); + aiTexture* texture = scene->mTextures[index]; + if (texture->mHeight == 0) { + int width, height, channels; + + unsigned char* data = stbi_load_from_memory( + reinterpret_cast(texture->pcData), + texture->mWidth, &width, &height, &channels, STBI_rgb_alpha); + + mesh.texture->tex_image_2d(RGBA, RGBA, GL_UNSIGNED_BYTE, data, + width, height); + stbi_image_free(data); + } else { + mesh.texture->tex_image_2d(RGBA, BGRA, GL_UNSIGNED_BYTE, + reinterpret_cast(texture->pcData), + texture->mWidth, texture->mHeight); + } + } else { + auto image_data = + Tools::load_image_data(texture_path.C_Str(), true, true); + + mesh.texture->tex_image_2d(RGBA, RGBA, GL_UNSIGNED_BYTE, + image_data.data, image_data.width, + image_data.height); + } + mesh.texture->set_nearest(); + return true; +} + +glm::mat4 ModelLoader::convert_matrix(const aiMatrix4x4& m) { + return glm::mat4(m.a1, m.b1, m.c1, m.d1, m.a2, m.b2, m.c2, m.d2, m.a3, m.b3, + m.c3, m.d3, m.a4, m.b4, m.c4, m.d4); +} + +} // namespace Cubed \ No newline at end of file diff --git a/src/tools/shader_tools.cpp b/src/tools/shader_tools.cpp index 7a4f489..e24f1aa 100644 --- a/src/tools/shader_tools.cpp +++ b/src/tools/shader_tools.cpp @@ -175,8 +175,14 @@ void delete_image_data(unsigned char* data) { stbi_image_free(data); } -ImageData load_image_data(const std::string& tex_image_path, bool check_exist) { - fs::path path = ASSETS_PATH + tex_image_path; +ImageData load_image_data(const std::string& tex_image_path, bool check_exist, + bool full_path) { + fs::path path; + if (full_path) { + path = tex_image_path; + } else { + path = ASSETS_PATH + tex_image_path; + } if (check_exist) { ASSERT_MSG(fs::is_regular_file(path), path.c_str()); }