extends SceneTree const ADT_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/adt_builder.gd") const BAKED_TILE_SCRIPT := preload("res://resources/baked_adt_tile.gd") var _builder var _loader var _image_cache: Dictionary = {} func _initialize() -> void: var args := OS.get_cmdline_user_args() var map_name := _get_arg_value(args, "--map", "Azeroth") var extracted_dir := _normalize_res_path(_get_arg_value(args, "--extracted", "res://extracted")) var output_dir := _normalize_res_path(_get_arg_value(args, "--output", "res://cache/baked_terrain_v2")) var legacy_texture_size = _get_optional_int_arg(args, "--texture-size") var full_texture_size := maxi( 256, _get_arg_value( args, "--full-texture-size", str(legacy_texture_size if legacy_texture_size != null else 2048) ).to_int() ) var coarse_texture_size := maxi( 128, _get_arg_value( args, "--coarse-texture-size", str(legacy_texture_size if legacy_texture_size != null else 512) ).to_int() ) var tile_x: Variant = _get_optional_int_arg(args, "--tile-x") var tile_y: Variant = _get_optional_int_arg(args, "--tile-y") var force := args.has("--force") _builder = ADT_BUILDER_SCRIPT.new() if not ClassDB.class_exists("ADTLoader"): push_error("ADTLoader not found. Rebuild GDExtension first.") quit(1) return _loader = ClassDB.instantiate("ADTLoader") if _loader == null: push_error("Failed to instantiate ADTLoader.") quit(1) return var map_dir := extracted_dir.path_join("World/Maps/%s" % map_name) var dir := DirAccess.open(map_dir) if dir == null: push_error("Cannot open map directory: %s" % map_dir) quit(1) return DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(output_dir.path_join(map_name))) var baked := 0 var skipped := 0 var failed := 0 var started_ms := Time.get_ticks_msec() for file_name in dir.get_files(): if not file_name.ends_with(".adt"): continue var stem := file_name.trim_suffix(".adt") var parts := stem.split("_") if parts.size() != 3 or parts[0] != map_name: continue if not parts[1].is_valid_int() or not parts[2].is_valid_int(): continue var tx := parts[1].to_int() var ty := parts[2].to_int() if tile_x != null and tx != tile_x: continue if tile_y != null and ty != tile_y: continue var source_res_path := map_dir.path_join(file_name) var source_abs_path := ProjectSettings.globalize_path(source_res_path) var output_res_path := output_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty]) var output_abs_path := ProjectSettings.globalize_path(output_res_path) if not force and FileAccess.file_exists(output_abs_path): skipped += 1 continue var data: Dictionary = _loader.call("load_adt", source_abs_path) if data.is_empty() or not data.has("chunks"): push_warning("Bake failed to parse ADT: %s" % source_res_path) failed += 1 continue var full_payload: Dictionary = _builder.build_baked_tile_render_payload( data, _image_cache, ProjectSettings.globalize_path(extracted_dir), 0, full_texture_size) var coarse_payload: Dictionary = _builder.build_baked_tile_render_payload( data, _image_cache, ProjectSettings.globalize_path(extracted_dir), 3, coarse_texture_size) if full_payload.is_empty(): push_warning("Bake produced empty full mesh: %s" % source_res_path) failed += 1 continue var baked_tile: Resource = BAKED_TILE_SCRIPT.new() baked_tile.set("format_version", BAKED_TILE_SCRIPT.FORMAT_VERSION) baked_tile.set("map_name", map_name) baked_tile.set("tile_x", tx) baked_tile.set("tile_y", ty) baked_tile.set("tile_origin", _builder.get_tile_origin_for_data(data)) baked_tile.set("full_texture_size", full_texture_size) baked_tile.set("coarse_texture_size", coarse_texture_size) baked_tile.set("wmo_names", data.get("wmo_names", PackedStringArray())) baked_tile.set("wmo_placements", data.get("wmo_placements", [])) baked_tile.set("m2_names", data.get("m2_names", PackedStringArray())) baked_tile.set("m2_placements", data.get("m2_placements", [])) baked_tile.set("full_mesh", full_payload.get("mesh", null)) baked_tile.set("coarse_mesh", coarse_payload.get("mesh", null)) var save_err := ResourceSaver.save(baked_tile, output_res_path) if save_err != OK: push_warning("Failed to save baked tile: %s (err=%d)" % [output_res_path, save_err]) failed += 1 continue baked += 1 if baked % 25 == 0: print("Baked %d tiles..." % baked) var elapsed_ms := Time.get_ticks_msec() - started_ms print("Bake finished. baked=%d skipped=%d failed=%d elapsed=%.2fs" % [ baked, skipped, failed, float(elapsed_ms) / 1000.0]) quit(0 if failed == 0 else 2) func _get_arg_value(args: PackedStringArray, name: String, default_value: String) -> String: var index := args.find(name) if index >= 0 and index + 1 < args.size(): return args[index + 1] return default_value func _get_optional_int_arg(args: PackedStringArray, name: String): var index := args.find(name) if index >= 0 and index + 1 < args.size() and args[index + 1].is_valid_int(): return args[index + 1].to_int() return null func _normalize_res_path(path: String) -> String: if path.begins_with("res://"): return path return "res://%s" % path.trim_prefix("./").trim_prefix("/")