This commit is contained in:
Nicholas Butzke
2026-08-02 02:20:02 -04:00
parent 61669627db
commit 922983429e
226 changed files with 34032 additions and 18521 deletions
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Create a compact animation-only GLB from selected actions in a source GLB.
Usage:
blender --background --python tools/extract_animation_subset.py -- \
<source.glb> <output.glb> <clip> [<clip> ...]
The source skeleton is retained, all meshes and unrelated actions are removed,
and each requested action is exported as its own NLA track. This keeps external
authored animation libraries small enough to ship as retargeting sources.
"""
import os
import sys
import bpy
argv = sys.argv
argv = argv[argv.index("--") + 1:] if "--" in argv else []
if len(argv) < 3:
print(
"Usage: blender --background --python "
"tools/extract_animation_subset.py -- "
"<source.glb> <output.glb> <clip> [<clip> ...]"
)
sys.exit(1)
SOURCE = argv[0]
OUTPUT = argv[1]
REQUESTED = argv[2:]
def main():
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete()
bpy.ops.import_scene.gltf(filepath=SOURCE)
armatures = [obj for obj in bpy.data.objects if obj.type == "ARMATURE"]
if len(armatures) != 1:
print(f"ERROR: expected one armature, found {len(armatures)}")
sys.exit(1)
armature = armatures[0]
missing = [name for name in REQUESTED if bpy.data.actions.get(name) is None]
if missing:
print(f"ERROR: requested actions not found: {missing}")
sys.exit(1)
for obj in list(bpy.data.objects):
if obj != armature:
bpy.data.objects.remove(obj, do_unlink=True)
if not armature.animation_data:
armature.animation_data_create()
for track in list(armature.animation_data.nla_tracks):
armature.animation_data.nla_tracks.remove(track)
armature.animation_data.action = None
keep = set()
for name in REQUESTED:
action = bpy.data.actions[name]
action.use_fake_user = True
track = armature.animation_data.nla_tracks.new()
track.name = name
strip = track.strips.new(name, 0, action)
strip.name = name
track.mute = True
keep.add(action)
for action in list(bpy.data.actions):
if action not in keep:
bpy.data.actions.remove(action)
os.makedirs(os.path.dirname(os.path.abspath(OUTPUT)), exist_ok=True)
bpy.ops.export_scene.gltf(
filepath=OUTPUT,
export_format="GLB",
export_yup=True,
export_animations=True,
export_animation_mode="NLA_TRACKS",
export_skins=True,
export_bake_animation=True,
)
print(f"Exported {len(keep)} clips to {OUTPUT}")
main()