From 60eaac805160a72a83db799f74b4237d54355cd6 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Thu, 12 Oct 2023 17:02:36 -0600 Subject: [PATCH] decompiler: add texture merging feature (#3083) --- decompiler/data/TextureDB.cpp | 30 ++++++++++++++++++++++++++++++ decompiler/data/TextureDB.h | 1 + decompiler/extractor/main.cpp | 9 +++++++++ decompiler/main.cpp | 7 +++++++ 4 files changed, 47 insertions(+) diff --git a/decompiler/data/TextureDB.cpp b/decompiler/data/TextureDB.cpp index 8981a0b24..8cfe905a8 100644 --- a/decompiler/data/TextureDB.cpp +++ b/decompiler/data/TextureDB.cpp @@ -99,6 +99,36 @@ void TextureDB::add_index_texture(u32 tpage, } } +void TextureDB::merge_textures(const fs::path& base_path) { + for (auto& tex : textures) { + fs::path full_path = base_path / tpage_names.at(tex.second.page) / (tex.second.name + ".png"); + if (fs::exists(full_path)) { + lg::info("Merging {}", full_path.string().c_str()); + int w, h; + auto merge_data = stbi_load(full_path.string().c_str(), &w, &h, 0, 4); // rgba channels + if (!merge_data) { + lg::warn("failed to load PNG file: {}", full_path.string().c_str()); + continue; + } else if (w != tex.second.w || h != tex.second.h) { + lg::warn("merge texture does not match the same dimensions: {}, {} != {} || {} != {}", + full_path.string().c_str(), w, tex.second.w, h, tex.second.h); + stbi_image_free(merge_data); + continue; + } + // Merge any non-transparent pixels into the existing texture + for (int i = 0; i < w * h * 4; i += 4) { + const auto merge_pixel_a = merge_data[i + 3]; + if (merge_pixel_a != 0) { + u32 merge_pixel; + memcpy(&merge_pixel, &merge_data[i], sizeof(u32)); + tex.second.rgba_bytes.at(i / 4) = merge_pixel; + } + } + stbi_image_free(merge_data); + } + } +} + void TextureDB::replace_textures(const fs::path& path) { fs::path base_path(path); for (auto& tex : textures) { diff --git a/decompiler/data/TextureDB.h b/decompiler/data/TextureDB.h index 1a933a922..27ff9eaf2 100644 --- a/decompiler/data/TextureDB.h +++ b/decompiler/data/TextureDB.h @@ -55,6 +55,7 @@ struct TextureDB { const std::string& tpage_name, const std::vector& level_names); + void merge_textures(const fs::path& base_path); void replace_textures(const fs::path& path); std::string generate_texture_dest_adjustment_table() const; diff --git a/decompiler/extractor/main.cpp b/decompiler/extractor/main.cpp index cbee12c67..e31cc6e8b 100644 --- a/decompiler/extractor/main.cpp +++ b/decompiler/extractor/main.cpp @@ -172,6 +172,15 @@ void decompile(const fs::path& iso_data_path, const std::string& data_subfolder) file_util::create_dir_if_needed(textures_out); file_util::write_text_file(textures_out / "tpage-dir.txt", db.process_tpages(tex_db, textures_out, config)); + + // texture merges + // TODO - put all this stuff in somewhere common + auto texture_merge_path = file_util::get_jak_project_dir() / "game" / "assets" / + game_version_names[config.game_version] / "texture_merges"; + if (fs::exists(texture_merge_path)) { + tex_db.merge_textures(texture_merge_path); + } + // texture replacements auto replacements_path = file_util::get_jak_project_dir() / "texture_replacements"; if (fs::exists(replacements_path)) { diff --git a/decompiler/main.cpp b/decompiler/main.cpp index 74f2426ea..7a3150a07 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -298,6 +298,13 @@ int main(int argc, char** argv) { mem_log("After textures: {} MB", get_peak_rss() / (1024 * 1024)); + // Merge textures before replacing them, in other words, replacements take priority + auto texture_merge_path = file_util::get_jak_project_dir() / "game" / "assets" / + game_version_names[config.game_version] / "texture_merges"; + if (fs::exists(texture_merge_path)) { + tex_db.merge_textures(texture_merge_path); + } + auto replacements_path = file_util::get_jak_project_dir() / "texture_replacements"; if (fs::exists(replacements_path)) { tex_db.replace_textures(replacements_path);