skills/blender-motion-capture/SKILL.md
Automate motion capture and tracking workflows in Blender with Python. Use when the user wants to import BVH or FBX mocap data, retarget motion to armatures, track camera or object motion from video, solve camera motion, clean up motion capture data, or script any tracking pipeline in Blender.
npx skillsauth add tusosos/manus-knowledge-base blender-motion-captureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Import, process, and retarget motion capture data in Blender using Python. Work with BVH/FBX mocap files, track camera and object motion from video footage, solve 3D camera paths, and clean up animation data — all scriptable from the terminal.
import bpy
bpy.ops.import_anim.bvh(
filepath="/path/to/mocap.bvh",
target='ARMATURE',
global_scale=1.0,
frame_start=1,
use_fps_scale=False,
rotate_mode='NATIVE',
axis_forward='-Z', axis_up='Y'
)
armature = bpy.context.active_object
action = armature.animation_data.action
print(f"Imported: {armature.name}, Bones: {len(armature.data.bones)}, Frames: {action.frame_range}")
bpy.ops.import_scene.fbx(
filepath="/path/to/mocap.fbx",
use_anim=True,
ignore_leaf_bones=True,
automatic_bone_orientation=True,
primary_bone_axis='Y', secondary_bone_axis='X'
)
from mathutils import Matrix
def retarget_motion(source_armature, target_armature, bone_mapping):
"""Retarget animation using a bone name mapping: {target_bone: source_bone}"""
source_action = source_armature.animation_data.action
frame_start, frame_end = int(source_action.frame_range[0]), int(source_action.frame_range[1])
if not target_armature.animation_data:
target_armature.animation_data_create()
new_action = bpy.data.actions.new(f"{source_action.name}_retarget")
target_armature.animation_data.action = new_action
for frame in range(frame_start, frame_end + 1):
bpy.context.scene.frame_set(frame)
for tgt_name, src_name in bone_mapping.items():
src = source_armature.pose.bones.get(src_name)
tgt = target_armature.pose.bones.get(tgt_name)
if not src or not tgt:
continue
tgt.rotation_quaternion = src.rotation_quaternion
tgt.keyframe_insert(data_path="rotation_quaternion", frame=frame)
# Copy location for root bone only
if src_name == list(bone_mapping.values())[0]:
tgt.location = src.location
tgt.keyframe_insert(data_path="location", frame=frame)
# Example Mixamo → Rigify mapping
mapping = {
"spine": "mixamorig:Hips", "spine.001": "mixamorig:Spine",
"spine.004": "mixamorig:Neck", "spine.006": "mixamorig:Head",
"upper_arm.L": "mixamorig:LeftArm", "forearm.L": "mixamorig:LeftForeArm",
"upper_arm.R": "mixamorig:RightArm", "forearm.R": "mixamorig:RightForeArm",
"thigh.L": "mixamorig:LeftUpLeg", "shin.L": "mixamorig:LeftLeg",
"thigh.R": "mixamorig:RightUpLeg", "shin.R": "mixamorig:RightLeg",
}
def decimate_fcurve(fcurve, factor=0.5):
"""Remove keyframes to reduce data while keeping shape."""
points = fcurve.keyframe_points
total = len(points)
keep_every = max(1, int(1.0 / factor))
remove_indices = [i for i in range(total) if i % keep_every != 0 and i != 0 and i != total - 1]
for i in reversed(remove_indices):
points.remove(points[i])
armature = bpy.context.active_object
action = armature.animation_data.action
for fcurve in action.fcurves:
decimate_fcurve(fcurve, factor=0.5)
fcurve.update()
# Load footage
clip = bpy.data.movieclips.load("/path/to/footage.mp4")
scene = bpy.context.scene
scene.active_clip = clip
# Configure tracking
tracking = clip.tracking
settings = tracking.settings
settings.default_pattern_size = 21
settings.default_search_size = 71
settings.default_motion_model = 'AFFINE'
# Camera settings for solving
camera = tracking.camera
camera.sensor_width = 36.0
camera.focal_length = 50.0
# Solve camera motion
bpy.ops.clip.solve_camera()
solve_error = tracking.reconstruction.average_error
print(f"Solve error: {solve_error:.4f} px ({'Good' if solve_error < 0.5 else 'Needs refinement'})")
# Set up scene from solved data
bpy.ops.clip.setup_tracking_scene()
obj = bpy.data.objects["MyObject"]
constraint = obj.constraints.new(type='FOLLOW_TRACK')
constraint.clip = clip
constraint.track = tracking.tracks["Marker_01"]
constraint.use_3d_position = True
constraint.camera = scene.camera
# Bake constraint to keyframes
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.nla.bake(
frame_start=1, frame_end=clip.frame_duration,
only_selected=True, visual_keying=True,
clear_constraints=True, bake_types={'OBJECT'}
)
# Export as BVH
bpy.ops.export_anim.bvh(
filepath="/tmp/output_mocap.bvh",
frame_start=int(action.frame_range[0]),
frame_end=int(action.frame_range[1]),
rotate_mode='NATIVE'
)
# Export as FBX with baked animation
bpy.ops.export_scene.fbx(
filepath="/tmp/output_anim.fbx",
use_selection=True, bake_anim=True,
bake_anim_use_all_bones=True, add_leaf_bones=False
)
User request: "Import all BVH files from a folder, list bone counts and frame ranges"
import bpy, glob, os
for filepath in sorted(glob.glob("/path/to/mocap_library/*.bvh")):
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
bpy.ops.import_anim.bvh(filepath=filepath, target='ARMATURE', global_scale=0.01, frame_start=1)
arm = bpy.context.active_object
if arm and arm.animation_data:
action = arm.animation_data.action
duration = (action.frame_range[1] - action.frame_range[0]) / bpy.context.scene.render.fps
print(f"{os.path.basename(filepath)}: {len(arm.data.bones)} bones, {duration:.1f}s")
Run: blender --background --python scan_mocap.py
User request: "Import a BVH file, apply it to my rigged character, and render a preview"
import bpy
bpy.ops.wm.open_mainfile(filepath="/path/to/character.blend")
char_armature = bpy.data.objects["Armature"]
bpy.ops.import_anim.bvh(filepath="/path/to/walk_cycle.bvh", target='ARMATURE', global_scale=0.01)
mocap_armature = bpy.context.active_object
mocap_action = mocap_armature.animation_data.action
# Transfer action (works when bone names match)
if not char_armature.animation_data:
char_armature.animation_data_create()
char_armature.animation_data.action = mocap_action
# Remove temp armature, set frame range, add camera, render
bpy.data.objects.remove(mocap_armature)
scene = bpy.context.scene
scene.frame_start, scene.frame_end = int(mocap_action.frame_range[0]), int(mocap_action.frame_range[1])
scene.render.filepath = "/tmp/mocap_preview/frame_"
bpy.ops.render.render(animation=True)
global_scale=0.01 for cm-based files.bpy.ops.nla.bake() to convert constraints to keyframes for export.bake_anim=True in FBX to flatten NLA strips and constraints.tools
Download video and audio from YouTube and other platforms with yt-dlp. Use when a user asks to download YouTube videos, extract audio from videos, download playlists, get subtitles, download specific formats or qualities, batch download, archive channels, extract metadata, embed thumbnails, download from social media platforms (Twitter, Instagram, TikTok), or build media ingestion pipelines. Covers format selection, audio extraction, playlists, subtitles, metadata, and automation.
development
Download YouTube videos with customizable quality and format options. Use this skill when the user asks to download, save, or grab YouTube videos. Supports various quality settings (best, 1080p, 720p, 480p, 360p), multiple formats (mp4, webm, mkv), and audio-only downloads as MP3.
development
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
development
Use when you have a spec or requirements for a multi-step task, before touching code