diff --git a/pipeline/batch_roster.py b/pipeline/batch_roster.py index b3cb4ec1..1636cf88 100644 --- a/pipeline/batch_roster.py +++ b/pipeline/batch_roster.py @@ -51,6 +51,39 @@ FIGHTERS = { kit={"idle": "Bouncing Fight Idle", "jab": "Punching (1)", "straight": "Hook Punch", "kick": "Kicking (5)", "special": "Armada", "finisher": "Standing Melee Attack Horizontal"}), + # -- MeshGod wave (TRELLIS fused meshes: torn="*" rips the whole texture) -- + "decks": dict(model="decks.fbx", torn="*", + kit={"idle": "Bouncing Fight Idle", "jab": "Elbow Punching", + "straight": "Cross Punch", "kick": "Kicking (2)", + "special": "Stylish Flip", "finisher": "Headbutt"}), + "birdie": dict(model="birdie.fbx", torn="*", + kit={"idle": "Ready Idle", "jab": "Punching", + "straight": "Hook Punch", "kick": "Knee Kick Lead", + "special": "Flip Kick (1)", "finisher": "Bayonet Stab"}), + "widow": dict(model="widow.fbx", torn="*", + kit={"idle": "Ready Idle", "jab": "Boxing (2)", + "straight": "Big Body Blow", "kick": "Kicking", + "special": "Inverted Double Kick To Kip Up", "finisher": "Strangling"}), + "violet": dict(model="violet.fbx", torn="*", + kit={"idle": "Bouncing Fight Idle", "jab": "Boxing (1)", + "straight": "Mutant Punch", "kick": "Leg Sweep", + "special": "Flip Kick", "finisher": "Standing Torch Melee Attack 01"}), + "nappo": dict(model="nappo.fbx", torn="*", + kit={"idle": "Bouncing Fight Idle", "jab": "Elbow Punch", + "straight": "Cross Punch", "kick": "Kicking (2)", + "special": "Front Twist Flip", "finisher": "Grab And Slam"}), + "dread": dict(model="dread.fbx", torn="*", + kit={"idle": "Ready Idle", "jab": "Punching", + "straight": "Hook Punch", "kick": "Kicking (6)", + "special": "Front Flip", "finisher": "Fireball"}), + "jima": dict(model="jima.fbx", torn="*", + kit={"idle": "Ginga Variation 3", "jab": "Boxing (2)", + "straight": "Big Body Blow", "kick": "Knee Kick Lead", + "special": "Macaco Side", "finisher": "Stable Sword Outward Slash"}), + "roca": dict(model="roca.fbx", torn="*", + kit={"idle": "Ready Idle", "jab": "Elbow Punching", + "straight": "Cross Punch", "kick": "Standing Melee Attack Kick Ver. 1", + "special": "Ginga Sideways To Au", "finisher": "Hell Slammer B"}), } diff --git a/pipeline/render_moves.py b/pipeline/render_moves.py index 746d1d9d..88e43017 100644 --- a/pipeline/render_moves.py +++ b/pipeline/render_moves.py @@ -137,7 +137,9 @@ def tear_clothing(objs, name_filter: str, seed: int): keys = [k.strip().lower() for k in name_filter.split(",") if k.strip()] done_images = set() for o in objs: - if o.type != "MESH" or not any(k in o.name.lower() for k in keys): + if o.type != "MESH": + continue + if "*" not in keys and not any(k in o.name.lower() for k in keys): continue for slot in o.material_slots: mat = slot.material diff --git a/tools/assess_fbx.py b/tools/assess_fbx.py new file mode 100644 index 00000000..32427cc8 --- /dev/null +++ b/tools/assess_fbx.py @@ -0,0 +1,63 @@ +# Blender headless: probe a folder of FBX models for fighter-readiness. +# Emits one JSON line per file: rig, bones, namespace, meshes, verts, +# height, textures, action count. Re-runnable (skips nothing, cheap). +# +# Usage: Blender -b --python tools/assess_fbx.py -- --dir ~/Documents/fbxes --out report.jsonl +import argparse +import json +import sys +from pathlib import Path + +import bpy + + +def parse_args(): + argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] + ap = argparse.ArgumentParser() + ap.add_argument("--dir", required=True) + ap.add_argument("--out", required=True) + return ap.parse_args(argv) + + +args = parse_args() +src = Path(args.dir).expanduser() +out = Path(args.out).expanduser() +results = [] + +for f in sorted(src.glob("*.fbx")): + rec = {"file": f.name, "mb": round(f.stat().st_size / 1e6, 1)} + bpy.ops.wm.read_factory_settings(use_empty=True) + try: + bpy.ops.import_scene.fbx(filepath=str(f)) + except Exception as e: + rec["error"] = str(e)[:80] + results.append(rec) + continue + arms = [o for o in bpy.data.objects if o.type == "ARMATURE"] + meshes = [o for o in bpy.data.objects if o.type == "MESH"] + rec["arms"] = len(arms) + rec["meshes"] = len(meshes) + rec["verts"] = sum(len(m.data.vertices) for m in meshes) + if arms: + bones = arms[0].data.bones + rec["bones"] = len(bones) + rec["ns"] = "" + for b in bones: + if ":" in b.name: + rec["ns"] = b.name.split(":")[0] + break + # world height + import mathutils + pts = [o.matrix_world @ mathutils.Vector(c) for o in meshes for c in o.bound_box] + if pts: + rec["h"] = round(max(p[2] for p in pts) - min(p[2] for p in pts), 2) + rec["textured"] = any( + n.type == "TEX_IMAGE" and n.image is not None + for m in meshes for s in m.material_slots if s.material and s.material.use_nodes + for n in s.material.node_tree.nodes) + rec["actions"] = len(bpy.data.actions) + results.append(rec) + print("PROBED", f.name, "rig" if arms else "NORIG", flush=True) + +out.write_text("\n".join(json.dumps(r) for r in results)) +print("WROTE", out, len(results), "records") diff --git a/tools/tune_fighters.py b/tools/tune_fighters.py index 2fbe939f..bba2e1b7 100644 --- a/tools/tune_fighters.py +++ b/tools/tune_fighters.py @@ -70,6 +70,14 @@ CHAR_TUNE = { "jinx": {"finisher": DEFAULT_FINISHER}, "elva": {"finisher": DEFAULT_FINISHER}, "umbra": {"finisher": DEFAULT_FINISHER}, + "decks": {"finisher": DEFAULT_FINISHER}, + "birdie": {"finisher": DEFAULT_FINISHER}, + "widow": {"finisher": DEFAULT_FINISHER}, + "violet": {"finisher": DEFAULT_FINISHER}, + "nappo": {"finisher": DEFAULT_FINISHER}, + "dread": {"finisher": DEFAULT_FINISHER}, + "jima": {"finisher": DEFAULT_FINISHER}, + "roca": {"finisher": DEFAULT_FINISHER}, } @@ -109,6 +117,14 @@ MANIFESTS = { "jinx": _roster_manifest("jinx", "JINX", 1000, 3.6, 2.8, -13.5, "#e05070"), "elva": _roster_manifest("elva", "ELVA", 900, 3.8, 3.1, -14.5, "#58c060"), "umbra": _roster_manifest("umbra", "UMBRA", 1200, 3.1, 2.5, -13.0, "#503a70"), + "decks": _roster_manifest("decks", "DECKS", 950, 3.7, 2.9, -14.0, "#8a4ae8"), + "birdie": _roster_manifest("birdie", "BIRDIE", 900, 3.8, 3.0, -14.0, "#e878b0"), + "widow": _roster_manifest("widow", "WIDOW", 1000, 3.3, 2.7, -13.2, "#383844"), + "violet": _roster_manifest("violet", "VIOLET", 950, 3.5, 2.8, -13.5, "#7a5ae0"), + "nappo": _roster_manifest("nappo", "NAPPO", 1000, 3.4, 2.7, -13.5, "#38b8a8"), + "dread": _roster_manifest("dread", "DREAD", 900, 3.9, 3.1, -14.2, "#d8a838"), + "jima": _roster_manifest("jima", "JIMA", 1050, 3.2, 2.6, -13.0, "#d8d8d8"), + "roca": _roster_manifest("roca", "ROCA", 1050, 3.3, 2.6, -13.0, "#b87848"), }