#include "static_object.h" #include "asset_io.h" #include "dxt.h" #include "gr2_bridge.h" #include "texture_util.h" #include #include #include #include #include #include #include #include using namespace godot; namespace mtgodot { namespace { std::unordered_map> g_dds_cache; // Building albedo (sRGB colour) -> mobile-ASTC-eligible via make_color_texture // (no-op on desktop). mipmaps=false keeps the desktop result byte-identical to // the old path (buildings had no mip chain). §F4. Ref decode_dds_cached(const std::string &real_path) { auto &cache = g_dds_cache; auto it = cache.find(real_path); if (it != cache.end()) return it->second; Ref tex; mtgodot::Image d = mtgodot::dds_from_file(godot::String(real_path.c_str())); if (d.ok()) { tex = mtgodot::make_color_texture(d.w, d.h, d.rgba.data(), d.rgba.size(), /*mipmaps=*/false); } cache.emplace(real_path, tex); return tex; } Ref material_for(const std::string &tex_name, bool alpha_blend, bool two_sided, const fmt::AssetResolver &res) { Ref mat; mat.instantiate(); mat->set_albedo(Color(0.7f, 0.7f, 0.7f)); mat->set_roughness(1.0f); mat->set_texture_filter(StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC); mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); mat->set_diffuse_mode(StandardMaterial3D::DIFFUSE_LAMBERT_WRAP); mat->set_specular_mode(StandardMaterial3D::SPECULAR_DISABLED); mat->set_feature(StandardMaterial3D::FEATURE_BACKLIGHT, true); mat->set_backlight(Color(0.2f, 0.2f, 0.2f)); String t = String(tex_name.c_str()).to_lower(); bool kw_alpha = t.find("leaf") != -1 || t.find("grass") != -1 || t.find("fence") != -1 || t.find("net") != -1 || t.find("ivy") != -1 || t.find("tree") != -1 || t.find("branch") != -1; if (alpha_blend) { // EterGrnLib TYPE_BLEND_PNT:真 alpha 混合(第 2 map 作 opacity) mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); } else if (kw_alpha) { mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_SCISSOR); mat->set_alpha_scissor_threshold(0.5f); mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); } if (two_sided) mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); if (tex_name.empty()) return mat; std::string rp = res.resolve(tex_name, nullptr); if (rp.empty()) { // gr2 里常是 .dds;有时资产用别的大小写 / 扩展。先只试原名。 return mat; } Ref tex = decode_dds_cached(rp); if (tex.is_valid()) { mat->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, tex); mat->set_albedo(Color(1, 1, 1)); mat->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); mat->set_texture(StandardMaterial3D::TEXTURE_EMISSION, tex); mat->set_emission(Color(1.0f, 1.0f, 1.0f)); mat->set_emission_energy_multiplier(0.18f); } return mat; } } // namespace Ref get_static_mesh(const std::string &real_gr2_path, const fmt::AssetResolver &res, StaticMeshCache &cache) { auto it = cache.by_path.find(real_gr2_path); if (it != cache.by_path.end()) return it->second; Ref result; // invalid until success gr2::LoadError err; auto loaded = mtgodot::gr2_from_file(godot::String(real_gr2_path.c_str()), &err); if (!loaded) { ++cache.failed; UtilityFunctions::push_warning(String("[static] gr2 load failed: ") + real_gr2_path.c_str() + " (" + err.message.c_str() + ")"); cache.by_path.emplace(real_gr2_path, result); return result; } const gr2::FileInfo &fi = loaded->file_info(); // 贴图名 -> 渲染态(来自 libgr2 dump_materials 的名字/map 推断) std::vector mats = gr2::dump_materials(*loaded); std::map> tex_state; // lower(tex) -> {alpha, two_sided} for (const auto &m : mats) { std::string k; for (char c : m.diffuse_texture) k += (char)std::tolower((unsigned char)c); if (!k.empty()) tex_state[k] = {m.alpha_blend, m.two_sided}; } std::vector parts = mtgodot::build_parts(fi); AABB bounds; Ref mesh = mtgodot::build_mesh(fi, parts, /*flip_winding=*/false, bounds); if (mesh.is_valid() && mesh->get_surface_count() > 0) { for (int s = 0; s < mesh->get_surface_count() && s < (int)parts.size(); ++s) { const mtgodot::RenderPart &rp = parts[s]; std::string tex; if (rp.mesh >= 0 && rp.mesh < (int)fi.meshes.size()) { const auto &mt = fi.meshes[rp.mesh].material_textures; if (rp.mat_index >= 0 && rp.mat_index < (int)mt.size()) tex = mt[rp.mat_index]; else if (!mt.empty()) tex = mt[0]; } bool alpha = false, two = false; { std::string k; for (char c : tex) k += (char)std::tolower((unsigned char)c); auto f = tex_state.find(k); if (f != tex_state.end()) { alpha = f->second.first; two = f->second.second; } } mesh->surface_set_material(s, material_for(tex, alpha, two, res)); } result = mesh; ++cache.loaded; } else { ++cache.failed; } cache.by_path.emplace(real_gr2_path, result); return result; } void cleanup_static_object_cache() { g_dds_cache.clear(); } } // namespace mtgodot