2V2-e.4: render 40250 pack terrain with map lighting

This commit is contained in:
shenlei
2026-09-23 21:19:14 +09:00
parent ac4c2aa4e0
commit cb43091f79
9 changed files with 226 additions and 24 deletions
+13 -1
View File
@@ -166,7 +166,19 @@ Ref<ImageTexture> load_texture(const std::string &name, const fmt::AssetResolver
image = godot::Image::create_from_data(decoded.w, decoded.h, false,
godot::Image::FORMAT_RGBA8, pixels);
} else {
image = godot::Image::load_from_file(godot_path);
// Image::load_from_file cannot open the pack:// virtual filesystem.
if (godot_path.begins_with("pack://")) {
PackedByteArray bytes = mtgodot::read_file(godot_path);
image.instantiate();
if (godot_path.get_extension().to_lower() == "tga")
image->load_tga_from_buffer(bytes);
else if (godot_path.get_extension().to_lower() == "png")
image->load_png_from_buffer(bytes);
else if (godot_path.get_extension().to_lower() == "jpg")
image->load_jpg_from_buffer(bytes);
} else {
image = godot::Image::load_from_file(godot_path);
}
}
if (image.is_null() || image->is_empty())
return Ref<ImageTexture>();
+26 -4
View File
@@ -278,6 +278,7 @@ void Metin2World::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_load_radius_tiles", "r"), &Metin2World::set_load_radius_tiles);
ClassDB::bind_method(D_METHOD("get_load_radius_tiles"), &Metin2World::get_load_radius_tiles);
ClassDB::bind_method(D_METHOD("set_focus_tile", "t"), &Metin2World::set_focus_tile);
ClassDB::bind_method(D_METHOD("set_initial_focus_cm", "position"), &Metin2World::set_initial_focus_cm);
ClassDB::bind_method(D_METHOD("get_focus_tile"), &Metin2World::get_focus_tile);
ClassDB::bind_method(D_METHOD("set_auto_load", "v"), &Metin2World::set_auto_load);
ClassDB::bind_method(D_METHOD("get_auto_load"), &Metin2World::get_auto_load);
@@ -290,6 +291,7 @@ void Metin2World::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_env_enabled", "v"), &Metin2World::set_env_enabled);
ClassDB::bind_method(D_METHOD("get_env_enabled"), &Metin2World::get_env_enabled);
ClassDB::bind_method(D_METHOD("set_water_enabled", "v"), &Metin2World::set_water_enabled);
ClassDB::bind_method(D_METHOD("set_collision_enabled", "v"), &Metin2World::set_collision_enabled);
ClassDB::bind_method(D_METHOD("get_water_enabled"), &Metin2World::get_water_enabled);
ClassDB::bind_method(D_METHOD("set_tree_shadows", "v"), &Metin2World::set_tree_shadows);
ClassDB::bind_method(D_METHOD("get_tree_shadows"), &Metin2World::get_tree_shadows);
@@ -401,7 +403,7 @@ bool Metin2World::build_chunk(int tx, int ty) {
if (!ss.layers.empty()) {
String smpath;
String sm = String(dir.c_str()) + "/shadowmap.dds";
if (FileAccess::file_exists(sm))
if (mtgodot::file_exists(sm))
smpath = sm;
Ref<ShaderMaterial> tm = build_chunk_terrain_material(
ss, texture_set, *resolver, smpath);
@@ -766,6 +768,12 @@ bool Metin2World::load_map() {
return false;
}
setting_ok = true;
if (load_radius >= 0 && initial_focus_cm != Vector2()) {
focus_tx = (int)std::floor((initial_focus_cm.x - setting.base_position_x) /
fmt::m2coord::CHUNK_CM);
focus_ty = (int)std::floor((initial_focus_cm.y - setting.base_position_y) /
fmt::m2coord::CHUNK_CM);
}
// AssetResolver 是地图对象、环境声属性和 splat 共用的资源索引。
// 即使关闭 splat,也要保留这条链路,避免环境声依赖渲染开关。
@@ -785,16 +793,23 @@ bool Metin2World::load_map() {
c = '/';
c = (char)std::tolower((unsigned char)c);
}
// Setting.txt may already name the textureset/ directory. The pack
// virtual root ends in '/', while a loose asset root does not.
if (ts_rel.rfind("textureset/", 0) == 0)
ts_rel.erase(0, sizeof("textureset/") - 1);
std::string tse;
if (fmt::parse_texture_set_file(root + "/textureset/" + ts_rel, texture_set, &tse))
const std::string texture_path = root == "pack://"
? root + "textureset/" + ts_rel : root + "/textureset/" + ts_rel;
if (fmt::parse_texture_set_file(texture_path, texture_set, &tse))
splat_ready = true;
else
UtilityFunctions::push_warning(String("[Metin2World] TextureSet: ") + tse.c_str());
}
// Property CRC 注册表(对象和环境声共用;不依赖 objects_enabled)。
// A 40250 pack is a virtual filesystem, not a directory to scan. The terrain
// and its textures resolve by pack path; property objects are a later port.
registry_ok = false;
{
if (root != "pack://") {
// resolver 已建:用它的文件清单,避免再 std::filesystem 扫盘(PCK 里扫不了)。
bool ok = resolver
? registry.scan_list(root, resolver->all_rel(), &re)
@@ -1108,6 +1123,13 @@ Dictionary Metin2World::get_load_report() const {
d["registry_ok"] = registry_ok;
d["registry_crcs"] = (int)registry.by_crc.size();
d["env_ok"] = env_ok;
d["character_light_enabled"] = env.dir_light.ch_enable;
d["character_light_direction"] = Vector3(env.dir_light.direction[0],
env.dir_light.direction[1], env.dir_light.direction[2]);
d["character_light_diffuse"] = Color(env.dir_light.ch_diffuse[0],
env.dir_light.ch_diffuse[1], env.dir_light.ch_diffuse[2], env.dir_light.ch_diffuse[3]);
d["character_light_ambient"] = Color(env.dir_light.ch_ambient[0],
env.dir_light.ch_ambient[1], env.dir_light.ch_ambient[2], env.dir_light.ch_ambient[3]);
d["fog_level"] = env.fog.fog_level;
d["objects_placed"] = objects_placed;
d["objects_skipped"] = objects_skipped;
+3
View File
@@ -60,6 +60,7 @@ public:
void set_load_radius_tiles(int r) { load_radius = r; }
int get_load_radius_tiles() const { return load_radius; }
void set_focus_tile(godot::Vector2i t);
void set_initial_focus_cm(godot::Vector2 position) { initial_focus_cm = position; }
godot::Vector2i get_focus_tile() const { return godot::Vector2i(focus_tx, focus_ty); }
void set_auto_load(bool v) { auto_load = v; }
bool get_auto_load() const { return auto_load; }
@@ -72,6 +73,7 @@ public:
void set_env_enabled(bool v) { env_enabled = v; }
bool get_env_enabled() const { return env_enabled; }
void set_water_enabled(bool v) { water_enabled = v; }
void set_collision_enabled(bool v) { collision_enabled = v; }
bool get_water_enabled() const { return water_enabled; }
// §3.3: static building/tree shadows are already baked into shadowmap.dds, so
// they don't cast realtime sun shadows by default (avoids double-darkening).
@@ -123,6 +125,7 @@ private:
godot::String map_path = "OutdoorA1/metin2_map_a1";
int load_radius = -1; // <0 = 全图
int focus_tx = 0, focus_ty = 0;
godot::Vector2 initial_focus_cm;
bool auto_load = true;
bool splat_enabled = true;
// §3.5: 每区块地形拆成 N×N patch(各自 MeshInstance),启用逐 patch 视锥剔除 +