big
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user