#include "Cubed/texture_manager.hpp" #include "Cubed/config.hpp" #include "Cubed/constants.hpp" #include "Cubed/map_table.hpp" #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/log.hpp" #include "Cubed/tools/shader_tools.hpp" namespace { constexpr int BLOCK_SIZE = 16; constexpr int BLOCK_NORMAL_SIZE = 128; constexpr int CROSS_PLANE_SIZE = 16; constexpr int BLOCK_ITEM_SIZE = 16; constexpr int UI_SIZE = 16; constexpr int BLOCK_STATUS_SIZE = 16; constexpr int SKIN_SIZE = 64; unsigned char* generate_flat_normal_map(int width = BLOCK_NORMAL_SIZE, int height = BLOCK_NORMAL_SIZE) { unsigned char* data = new unsigned char[width * height * 4]; for (int i = 0; i < width * height; i++) { data[i * 4 + 0] = 128; // R -> X = 0 data[i * 4 + 1] = 128; // G -> Y = 0 data[i * 4 + 2] = 255; // B -> Z = 1 data[i * 4 + 3] = 255; // A } return data; } } // namespace namespace Cubed { TextureManager::TextureManager() {} TextureManager::~TextureManager() { delet_texture(); } void TextureManager::delet_texture() { if (m_init) { glDeleteTextures(1, &m_texture_array); glDeleteTextures(1, &m_block_status_array); glDeleteTextures(1, &m_cross_plane_array); glDeleteTextures(1, &m_normal_texture_array); for (auto& id : m_item_textures) { glDeleteTextures(1, &id); } glDeleteTextures(1, &m_skin); Logger::info("Successfully delete all texture"); } } GLuint TextureManager::get_block_status_array() const { return m_block_status_array; } GLuint TextureManager::get_texture_array() const { return m_texture_array; } GLuint TextureManager::get_cross_plane_array() const { return m_cross_plane_array; } GLuint TextureManager::get_ui_array() const { return m_ui_array; } GLuint TextureManager::get_pbr_texture() const { return m_normal_texture_array; } const std::vector& TextureManager::item_textures() const { return m_item_textures; } GLuint TextureManager::get_skin() const { return m_skin; } void TextureManager::load_block_status(unsigned id) { ASSERT_MSG(id < MAX_BLOCK_STATUS, "Exceed the max status sum limit"); std::string path = "texture/status/" + std::to_string(id) + ".png"; unsigned char* image_data = nullptr; image_data = (Tools::load_image_data(path)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, BLOCK_STATUS_SIZE, BLOCK_STATUS_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); } void TextureManager::load_block_texture(unsigned id) { ASSERT_MSG(id < BlockManager::sums(), "Exceed the max block sum limit"); std::string name{BlockManager::name_form_id(id)}; // air don`t need texture if (id == 0) { return; } if (BlockManager::is_cross_plane(id)) { load_cross_plane_texture(id); return; } unsigned char* image_data[6]; std::string block_texture_path = "texture/block/" + name; image_data[0] = (Tools::load_image_data(block_texture_path + "/front.png")); image_data[1] = (Tools::load_image_data(block_texture_path + "/right.png")); image_data[2] = (Tools::load_image_data(block_texture_path + "/back.png")); image_data[3] = (Tools::load_image_data(block_texture_path + "/left.png")); image_data[4] = (Tools::load_image_data(block_texture_path + "/top.png")); image_data[5] = (Tools::load_image_data(block_texture_path + "/base.png")); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); Tools::check_opengl_error(); for (int i = 0; i < 6; i++) { glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, BLOCK_SIZE, BLOCK_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data[i]); Tools::check_opengl_error(); Tools::delete_image_data(image_data[i]); } } void TextureManager::load_block_item_texture(unsigned id) { ASSERT_MSG(id < BlockManager::sums(), "Exceed the max block sum limit"); std::string name{BlockManager::name_form_id(id)}; std::string path = "texture/item/block/" + name + ".png"; unsigned char* data = nullptr; data = Tools::load_image_data(path); GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BLOCK_ITEM_SIZE, BLOCK_ITEM_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } m_item_textures.push_back(texture); Tools::delete_image_data(data); } void TextureManager::load_cross_plane_texture(unsigned id) { std::string path = "texture/block/" + BlockManager::name_form_id(id) + "/cross.png"; unsigned char* image_data = Tools::load_image_data(path); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, BlockManager::cross_plane_index(id), CROSS_PLANE_SIZE, CROSS_PLANE_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); } void TextureManager::load_ui_texture(unsigned id) { ASSERT_MSG(id < MAX_UI_NUM, "Exceed the max ui sum limit"); std::string path = "texture/ui/" + std::to_string(id) + ".png"; unsigned char* image_data = nullptr; image_data = (Tools::load_image_data(path)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, UI_SIZE, UI_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); } void TextureManager::load_pbr_texture(unsigned id) { if (id == 0) { return; } if (BlockManager::is_cross_plane(id)) { return; } std::string path = "normal/block/" + BlockManager::name_form_id(id); unsigned char* image_data[6]; image_data[0] = (Tools::load_image_data(path + "/front_n.png", false)); image_data[1] = (Tools::load_image_data(path + "/right_n.png", false)); image_data[2] = (Tools::load_image_data(path + "/back_n.png", false)); image_data[3] = (Tools::load_image_data(path + "/left_n.png", false)); image_data[4] = (Tools::load_image_data(path + "/top_n.png", false)); image_data[5] = (Tools::load_image_data(path + "/base_n.png", false)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_normal_texture_array); for (int i = 0; i < 6; i++) { unsigned char* data = image_data[i]; bool is_fallback = false; if (!data) { is_fallback = true; data = generate_flat_normal_map(); } glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, BLOCK_NORMAL_SIZE, BLOCK_NORMAL_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); if (is_fallback) { delete[] data; } else { Tools::delete_image_data(image_data[i]); } } } void TextureManager::init_block() { glGenTextures(1, &m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_SIZE, BLOCK_SIZE, BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glGenTextures(1, &m_cross_plane_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, CROSS_PLANE_SIZE, CROSS_PLANE_SIZE, BlockManager::cross_plane_sum(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glGenTextures(1, &m_normal_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_normal_texture_array); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, BLOCK_NORMAL_SIZE, BLOCK_NORMAL_SIZE, BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); for (unsigned i = 0; i < BlockManager::sums(); i++) { load_block_texture(i); load_block_item_texture(i); load_pbr_texture(i); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_normal_texture_array); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } Logger::info("Block Texture Load Success"); } void TextureManager::init_ui() { glGenTextures(1, &m_ui_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, UI_SIZE, UI_SIZE, MAX_UI_NUM, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); for (int i = 0; i < MAX_UI_NUM; i++) { load_ui_texture(i); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } void TextureManager::init_skin() { glGenTextures(1, &m_skin); glBindTexture(GL_TEXTURE_2D, m_skin); std::string path = "texture/skin/player001.png"; unsigned char* image_data = nullptr; image_data = (Tools::load_image_data(path)); glBindTexture(GL_TEXTURE_2D, m_skin); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SKIN_SIZE, SKIN_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); Tools::delete_image_data(image_data); glBindTexture(GL_TEXTURE_2D, m_skin); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } } void TextureManager::init_block_status() { glGenTextures(1, &m_block_status_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_STATUS_SIZE, BLOCK_STATUS_SIZE, MAX_BLOCK_STATUS, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); for (int i = 0; i < MAX_BLOCK_STATUS; i++) { load_block_status(i); } glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); if (m_aniso >= 1) { glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, static_cast(m_aniso)); } } void TextureManager::init_texture() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &m_max_aniso); if (m_max_aniso > 0.0f) { Logger::info("Support anisotropic filtering max_aniso is {}", m_max_aniso); } m_aniso = Config::get().get("texture.aniso"); m_aniso = std::min(static_cast(m_max_aniso), m_aniso); Logger::info("Setting Texture Aniso is {}", m_aniso); MapTable::init_map(); Logger::info("Map Init Success"); init_block(); init_block_status(); init_ui(); init_skin(); m_init = true; } void TextureManager::update() { if (m_need_reload) { hot_reload(); } } void TextureManager::need_reload() { m_need_reload = true; } void TextureManager::hot_reload() { delet_texture(); init_texture(); m_need_reload = false; } int TextureManager::max_aniso() const { return static_cast(m_max_aniso); } } // namespace Cubed