first commit
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
## Pre-bakes M2 doodad models into PackedScene (.tscn) files for fast runtime loading.
|
||||
##
|
||||
## Usage:
|
||||
## godot --headless --path <project> --script res://tools/bake_m2_cache.gd -- \
|
||||
## --map Azeroth --extracted res://extracted --output res://cache/m2_glb --force
|
||||
extends SceneTree
|
||||
|
||||
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
||||
|
||||
func _initialize() -> void:
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var map_name := _arg(args, "--map", "Azeroth")
|
||||
var extracted := _res(_arg(args, "--extracted", "res://extracted"))
|
||||
var output_dir := _res(_arg(args, "--output", "res://cache/m2_glb"))
|
||||
var force := args.has("--force")
|
||||
|
||||
if not ClassDB.class_exists("ADTLoader") or not ClassDB.class_exists("M2Loader"):
|
||||
push_error("GDExtension not loaded. Rebuild first.")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(output_dir))
|
||||
|
||||
# Collect unique M2 paths from all ADT tiles
|
||||
var unique: Dictionary = {}
|
||||
var adt_loader = ClassDB.instantiate("ADTLoader")
|
||||
var map_dir := extracted.path_join("World/Maps/%s" % map_name)
|
||||
var dir := DirAccess.open(map_dir)
|
||||
if dir == null:
|
||||
push_error("Cannot open map dir: %s" % map_dir)
|
||||
quit(1)
|
||||
return
|
||||
|
||||
print("Scanning ADT tiles...")
|
||||
var scanned := 0
|
||||
for fname in dir.get_files():
|
||||
if not fname.ends_with(".adt"):
|
||||
continue
|
||||
var parts := fname.trim_suffix(".adt").split("_")
|
||||
if parts.size() != 3 or parts[0] != map_name:
|
||||
continue
|
||||
var data: Dictionary = adt_loader.call("load_adt",
|
||||
ProjectSettings.globalize_path(map_dir.path_join(fname)))
|
||||
if data.is_empty():
|
||||
continue
|
||||
for rel in data.get("m2_names", PackedStringArray()):
|
||||
var norm := str(rel).replace("\\", "/").to_lower()
|
||||
if norm.ends_with(".mdx") or norm.ends_with(".mdl"):
|
||||
norm = norm.get_basename() + ".m2"
|
||||
if not norm.is_empty():
|
||||
unique[norm] = true
|
||||
scanned += 1
|
||||
|
||||
print("Found %d unique M2 models from %d tiles." % [unique.size(), scanned])
|
||||
|
||||
var m2_loader = ClassDB.instantiate("M2Loader")
|
||||
var baked := 0
|
||||
var skipped := 0
|
||||
var failed := 0
|
||||
var total := unique.size()
|
||||
var i := 0
|
||||
|
||||
for rel_path in unique.keys():
|
||||
i += 1
|
||||
var stem: String = rel_path.get_basename()
|
||||
var out_path := output_dir.path_join(stem + ".tscn")
|
||||
|
||||
if not force and ResourceLoader.exists(out_path):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
var abs_m2 := ProjectSettings.globalize_path(extracted.path_join(rel_path))
|
||||
if not FileAccess.file_exists(abs_m2):
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
var data: Dictionary = m2_loader.call("load_m2", abs_m2)
|
||||
if data.is_empty() or data.get("vertices", PackedVector3Array()).is_empty():
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
var node: Node3D = M2_BUILDER_SCRIPT.build(data, extracted)
|
||||
if node == null:
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
_set_owner_recursive(node, node)
|
||||
var scene := PackedScene.new()
|
||||
var err := scene.pack(node)
|
||||
node.free()
|
||||
if err != OK:
|
||||
push_warning("pack failed for %s: %d" % [rel_path, err])
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
DirAccess.make_dir_recursive_absolute(
|
||||
ProjectSettings.globalize_path(out_path.get_base_dir()))
|
||||
err = ResourceSaver.save(scene, out_path)
|
||||
if err != OK:
|
||||
push_warning("save failed for %s: %d" % [out_path, err])
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
baked += 1
|
||||
if i % 50 == 0 or i == total:
|
||||
print("[%d/%d] baked=%d skipped=%d failed=%d" % [i, total, baked, skipped, failed])
|
||||
|
||||
print("Done. baked=%d skipped=%d failed=%d" % [baked, skipped, failed])
|
||||
quit(0)
|
||||
|
||||
|
||||
func _arg(args: PackedStringArray, name: String, default: String) -> String:
|
||||
var idx := args.find(name)
|
||||
if idx >= 0 and idx + 1 < args.size():
|
||||
return args[idx + 1]
|
||||
return default
|
||||
|
||||
|
||||
func _res(path: String) -> String:
|
||||
if path.begins_with("res://") or path.begins_with("user://"):
|
||||
return path
|
||||
return "res://" + path.trim_prefix("./").trim_prefix("/")
|
||||
|
||||
|
||||
func _set_owner_recursive(node: Node, owner_root: Node) -> void:
|
||||
for child in node.get_children():
|
||||
child.owner = owner_root
|
||||
_set_owner_recursive(child, owner_root)
|
||||
Reference in New Issue
Block a user