2 Commits

24 changed files with 397 additions and 12 deletions

View File

@@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME}
OpenAL::OpenAL OpenAL::OpenAL
harfbuzz::harfbuzz harfbuzz::harfbuzz
Opus::Opus Opus::Opus
assimp::assimp
$<$<PLATFORM_ID:Windows>:ws2_32> $<$<PLATFORM_ID:Windows>:ws2_32>
) )

Binary file not shown.

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -10,7 +10,7 @@ find_package(harfbuzz REQUIRED)
find_package(Freetype REQUIRED) find_package(Freetype REQUIRED)
find_package(SDL3 REQUIRED) find_package(SDL3 REQUIRED)
find_package(Opus REQUIRED) find_package(Opus REQUIRED)
find_package(assimp REQUIRED)
# Third-party libraries # Third-party libraries
if (WIN32) if (WIN32)

View File

@@ -3,6 +3,7 @@
#include "Cubed/audio/audio_engine.hpp" #include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp" #include "Cubed/config.hpp"
#include "Cubed/dev_panel.hpp" #include "Cubed/dev_panel.hpp"
#include "Cubed/render/model_manager.hpp"
#include "Cubed/render/renderer.hpp" #include "Cubed/render/renderer.hpp"
#include "Cubed/scene/scene_manager.hpp" #include "Cubed/scene/scene_manager.hpp"
#include "Cubed/texture_manager.hpp" #include "Cubed/texture_manager.hpp"
@@ -49,7 +50,7 @@ private:
Window m_window; Window m_window;
TextureManager m_texture_manager; TextureManager m_texture_manager;
ModelManager m_model_manager;
AudioEngine m_audio; AudioEngine m_audio;
Renderer m_renderer; Renderer m_renderer;

View File

@@ -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<std::string, ModelNode> m_models;
const ModelNode& load_model(const std::string& model_name);
};
} // namespace Cubed

View File

@@ -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 <glad/glad.h>
#include <glm/glm.hpp>
#include <memory>
#include <string>
namespace Cubed {
struct Mesh {
std::vector<Vertex3D> vertices;
std::vector<uint32_t> indices;
std::unique_ptr<VertexBuffer> vbo;
std::unique_ptr<VertexBuffer> ebo;
std::unique_ptr<VertexArray> vao;
std::unique_ptr<Texture> texture;
void upload() {
vao = std::make_unique<VertexArray>();
vao->bind();
vbo = std::make_unique<VertexBuffer>();
vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D));
ebo = std::make_unique<VertexBuffer>(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<Mesh> meshes;
std::vector<ModelNode> children;
};
} // namespace Cubed

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Cubed/render/model_node.hpp"
#include "Cubed/shader.hpp"
#include <glm/glm.hpp>
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

View File

@@ -4,6 +4,7 @@
#include "Cubed/constants.hpp" #include "Cubed/constants.hpp"
#include "Cubed/input/event.hpp" #include "Cubed/input/event.hpp"
#include "Cubed/primitive_data.hpp" #include "Cubed/primitive_data.hpp"
#include "Cubed/render/model_renderer.hpp"
#include "Cubed/render/player_renderer.hpp" #include "Cubed/render/player_renderer.hpp"
#include "Cubed/render/shader_manager.hpp" #include "Cubed/render/shader_manager.hpp"
#include "Cubed/render/vertex_array.hpp" #include "Cubed/render/vertex_array.hpp"
@@ -19,12 +20,14 @@
namespace Cubed { namespace Cubed {
class TextureManager; class TextureManager;
class ClientWorld; class ClientWorld;
class ModelManager;
class DevPanel; class DevPanel;
class Renderer { class Renderer {
public: public:
constexpr static int NUM_VAO = 7; constexpr static int NUM_VAO = 7;
Renderer(TextureManager& texture_manager, Config& config); Renderer(TextureManager& texture_manager, Config& config,
ModelManager& model_manager);
~Renderer(); ~Renderer();
void reload_config(); void reload_config();
void init(); void init();
@@ -79,9 +82,14 @@ public:
bool handle_event(const Event& e); bool handle_event(const Event& e);
void render_model(const std::string& name, const glm::vec3& pos,
Camera& camera);
ModelManager& model_manager();
private: private:
TextureManager& m_texture_manager; TextureManager& m_texture_manager;
ModelManager& m_model_manager;
bool m_init = false; bool m_init = false;
float m_aspect = 0.0f; float m_aspect = 0.0f;
@@ -118,6 +126,7 @@ private:
std::vector<Vertex2D> m_ui; std::vector<Vertex2D> m_ui;
WorldRenderer m_world_renderer; WorldRenderer m_world_renderer;
ModelRender m_model_renderer;
Config& m_config; Config& m_config;
bool handle_window_resize_event(const WindowResizeEvent& e); bool handle_window_resize_event(const WindowResizeEvent& e);

View File

@@ -38,9 +38,11 @@ enum TextureFormat : GLenum {
R8 = GL_R8, R8 = GL_R8,
RGB = GL_RGB, RGB = GL_RGB,
RGBA8 = GL_RGBA8, RGBA8 = GL_RGBA8,
BGRA = GL_BGRA
}; };
// You need to set the texture scaling method, otherwise it will render as
// black.
class Texture { class Texture {
public: public:
explicit Texture(TextureType type); explicit Texture(TextureType type);

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Cubed/render/model_node.hpp"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
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

View File

@@ -47,7 +47,7 @@ bool check_opengl_error();
std::string read_shader_source(const std::string& file_path); std::string read_shader_source(const std::string& file_path);
ImageData load_image_data(const std::string& tex_image_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 } // namespace Tools

View File

@@ -92,4 +92,7 @@ target_sources(${PROJECT_NAME}
ui/row_layout.cpp ui/row_layout.cpp
ui/item_slot.cpp ui/item_slot.cpp
ui/inventory_ui.cpp ui/inventory_ui.cpp
tools/model_loader.cpp
render/model_manager.cpp
render/model_renderer.cpp
) )

View File

@@ -20,7 +20,8 @@ App::App()
: m_game_config(ASSETS_PATH "config.toml"), m_window(m_game_config), : m_game_config(ASSETS_PATH "config.toml"), m_window(m_game_config),
m_texture_manager(m_game_config), m_audio(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() { App::~App() {
stop_text_input(); stop_text_input();
@@ -85,6 +86,7 @@ void App::init(int argc, char** argv) {
Logger::info("Renderer Init Success"); Logger::info("Renderer Init Success");
// MapTable::init_map(); // MapTable::init_map();
m_texture_manager.init_texture(); m_texture_manager.init_texture();
m_model_manager.init();
Logger::info("Texture Load Success"); Logger::info("Texture Load Success");
m_scene_manager.request_push(SceneType::MAIN_MENU); m_scene_manager.request_push(SceneType::MAIN_MENU);

View File

@@ -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

View File

@@ -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<GLsizei>(mesh.indices.size()),
GL_UNSIGNED_INT, 0);
}
} // namespace Cubed

View File

@@ -15,9 +15,10 @@
namespace Cubed { namespace Cubed {
Renderer::Renderer(TextureManager& texture_manager, Config& config) Renderer::Renderer(TextureManager& texture_manager, Config& config,
: m_texture_manager(texture_manager), m_world_renderer(*this), ModelManager& model_manager)
m_config(config) {} : m_texture_manager(texture_manager), m_model_manager(model_manager),
m_world_renderer(*this), m_model_renderer(*this), m_config(config) {}
Renderer::~Renderer() { Renderer::~Renderer() {
if (m_init) { if (m_init) {
@@ -249,6 +250,12 @@ void Renderer::render_dev_panel(DevPanel& dev_panel) {
glEnable(GL_DEPTH_TEST); 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) { bool Renderer::handle_event(const Event& e) {
return std::visit(Overloaded{[](const MouseMoveEvent&) { return false; }, return std::visit(Overloaded{[](const MouseMoveEvent&) { return false; },
[](const MouseButtonEvent&) { 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; } float Renderer::frame_width() const { return m_frame_width; }
const glm::mat4& Renderer::p_mat() const { return m_world_proj_matrix; } const glm::mat4& Renderer::p_mat() const { return m_world_proj_matrix; }
const std::vector<VertexArray>& Renderer::vao() const { return m_vao; } const std::vector<VertexArray>& Renderer::vao() const { return m_vao; }
ModelManager& Renderer::model_manager() { return m_model_manager; }
} // namespace Cubed } // namespace Cubed

View File

@@ -35,6 +35,8 @@ void ShaderManager::init() {
"shaders/depth_player_fragment_shader.glsl"); "shaders/depth_player_fragment_shader.glsl");
register_shader("rect", "shaders/rect_v_shader.glsl", register_shader("rect", "shaders/rect_v_shader.glsl",
"shaders/rect_f_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, void ShaderManager::register_shader(const std::string& name,

View File

@@ -65,6 +65,7 @@ void WorldScene::render(Renderer& renderer) {
return; return;
} }
renderer.render_world(m_client_world); renderer.render_world(m_client_world);
renderer.render_model("model/creature/pig.glb", {0, 100, 0}, m_camera);
if (m_show_hud) { if (m_show_hud) {
m_hud_ui.render(renderer); m_hud_ui.render(renderer);
} }

124
src/tools/model_loader.cpp Normal file
View File

@@ -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 <assimp/postprocess.h>
#include <stb_image.h>
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<Texture>(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<unsigned char*>(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<char*>(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

View File

@@ -175,8 +175,14 @@ void delete_image_data(unsigned char* data) {
stbi_image_free(data); stbi_image_free(data);
} }
ImageData load_image_data(const std::string& tex_image_path, bool check_exist) { ImageData load_image_data(const std::string& tex_image_path, bool check_exist,
fs::path path = ASSETS_PATH + tex_image_path; bool full_path) {
fs::path path;
if (full_path) {
path = tex_image_path;
} else {
path = ASSETS_PATH + tex_image_path;
}
if (check_exist) { if (check_exist) {
ASSERT_MSG(fs::is_regular_file(path), path.c_str()); ASSERT_MSG(fs::is_regular_file(path), path.c_str());
} }

View File

@@ -63,6 +63,7 @@ void CreditsUI::init() {
add_text("HarfBuzz Old-MIT / GPL-2.0+"); add_text("HarfBuzz Old-MIT / GPL-2.0+");
add_text("utf8cpp BSL-1.0"); add_text("utf8cpp BSL-1.0");
add_text("stb - public domain / MIT licensed"); add_text("stb - public domain / MIT licensed");
add_text("assimp - BSD-3-Clause");
add_text("Music", 0.8f); add_text("Music", 0.8f);
add_text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), CC " add_text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), CC "
"BY 4.0."); "BY 4.0.");

View File

@@ -6,6 +6,7 @@
"harfbuzz", "harfbuzz",
"freetype", "freetype",
"opus", "opus",
"assimp",
{ {
"name": "sdl3", "name": "sdl3",
"default-features": false "default-features": false