// This file is part of Noggit3, licensed under GNU General Public License (version 3). #include "LiquidTextureManager.hpp" #include "opengl/context.inl" #include "noggit/DBC.h" #include "noggit/application/NoggitApplication.hpp" #include using namespace Noggit::Rendering; LiquidTextureManager::LiquidTextureManager(Noggit::NoggitRenderContext context) : _context(context) { } void LiquidTextureManager::upload() { if (_uploaded) return; for (int i = 0; i < gLiquidTypeDB.getRecordCount(); ++i) { const DBCFile::Record record = gLiquidTypeDB.getRecord(i); unsigned liquid_type_id = record.getInt(LiquidTypeDB::ID); int type = record.getInt(LiquidTypeDB::Type); glm::vec2 anim = {record.getFloat(LiquidTypeDB::AnimationX), record.getFloat(LiquidTypeDB::AnimationY)}; int shader_type = record.getInt(LiquidTypeDB::ShaderType); std::string filename; // procedural water hack fix if (shader_type == 3) { filename = "XTextures\\river\\lake_a."; // default param for water anim = glm::vec2(1.f, 0.f); } else [[likely]] { // TODO: why even try-catching there? empty string? BARE_EXCEPT_INV try { std::string db_string_template = record.getString(LiquidTypeDB::TextureFilenames); filename = db_string_template.substr(0, db_string_template.length() - 6); } catch (...) // fallback for malformed DBC { filename = "XTextures\\river\\lake_a."; } } GLuint array = 0; gl.genTextures(1, &array); gl.bindTexture(GL_TEXTURE_2D_ARRAY, array); // init 2D texture array // loading a texture is required to get its dimensions and format blp_texture tex(filename + "1.blp", _context); tex.finishLoading(); int width_ = tex.width(); int height_ = tex.height(); const unsigned mip_level = tex.mip_level(); const bool is_uncompressed = !tex.compression_format(); constexpr unsigned N_FRAMES = 30; if (is_uncompressed) { for (unsigned int j = 0; j < mip_level; ++j) { gl.texImage3D(GL_TEXTURE_2D_ARRAY, j, GL_RGBA8, width_, height_, N_FRAMES, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); width_ = std::max(width_ >> 1, 1); height_ = std::max(height_ >> 1, 1); } } else [[likely]] { for (unsigned int j = 0; j < mip_level; ++j) { gl.compressedTexImage3D(GL_TEXTURE_2D_ARRAY, j, tex.compression_format().value(), width_, height_, N_FRAMES, 0, static_cast(tex.compressed_data()[j].size() * N_FRAMES), nullptr); width_ = std::max(width_ >> 1, 1); height_ = std::max(height_ >> 1, 1); } } unsigned n_frames = 30; for (int j = 0; j < N_FRAMES; ++j) { if (!Noggit::Application::NoggitApplication::instance()->clientData()->exists(filename + std::to_string((j + 1)) + ".blp")) { n_frames = j; break; } blp_texture tex_frame(filename + std::to_string(j + 1) + ".blp", _context); tex_frame.finishLoading(); // error checking if (tex_frame.height() != tex.height() || tex_frame.width() != tex.width()) LogError << "Liquid texture resolution mismatch. Make sure all textures within a liquid type use identical format." << std::endl; else if (tex_frame.compression_format() != tex.compression_format()) LogError << "Liquid texture compression mismatch. Make sure all textures within a liquid type use identical format." << std::endl; else if (tex_frame.mip_level() != tex.mip_level()) LogError << "Liquid texture mip level mismatch. Make sure all textures within a liquid type use identical format." << std::endl; else [[likely]] { tex_frame.uploadToArray(j); continue; } // use the first frame, the texture will end-up non-animated or skipping certain frames, // but that avoids OpenGL errors. tex.uploadToArray(j); } gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mip_level - 3); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); _texture_frames_map[liquid_type_id] = std::make_tuple(array, anim, type, n_frames); } _uploaded = true; } void LiquidTextureManager::unload() { for (auto& pair : _texture_frames_map) { GLuint array = std::get<0>(pair.second); if (QOpenGLContext::currentContext()) { gl.deleteTextures(1, &array); } else { gl.scheduleCleanup([array]() mutable { gl.deleteTextures(1, &array); } ); } } _texture_frames_map.clear(); _uploaded = false; }