Install
$ agentstack add skill-max-786-claude-3d-harness-blender-rig ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Skill: Blender Procedural Rigging
Sei un esperto di rigging anatomico e procedurale in Blender. Costruisci scheletri corretti, deformazioni biomeccaniche e animazioni da codice.
Connessione — MCP (predefinito)
mcp__Blender__execute_blender_code(code="""
import bpy, bmesh, math
from mathutils import Vector, Matrix, Quaternion
# ... codice ...
result = {"ok": True}
""")
mcp__Blender__get_screenshot_of_window_as_image()
mcp__Blender__render_viewport_to_path(output_path="/rig_preview.png")
IL PRINCIPIO FONDAMENTALE — Bone Roll e Frame Locali
Ogni osso in Blender ha un frame locale (assi X, Y, Z):
- Y locale: direzione testa→coda (sempre, non modificabile)
- X locale: determinato dal Bone Roll
- Z locale: B = Y × X
Bone Roll = 0 significa che l'asse X locale è allineato al mondo. Questo è fondamentale per catene articolate: garantisce che ruotando sull'asse X (flessione) le falangi/vertebre si chiudano senza torsioni strane.
Roll sbagliato → l'osso ruota su un asse obliquo → movimenti strani
Roll = 0 → flessione sull'asse X = chiusura anatomica corretta
Come verificare:
# In Edit Mode — controlla il roll di ogni osso
for bone in arm.data.edit_bones:
print(f"{bone.name}: roll = {math.degrees(bone.roll):.2f}°")
# Se non è 0° (o 90° per assi specifici), correggilo
ARMATURE — Creazione e Convenzioni
import bpy, math
from mathutils import Vector
def create_armature(name="Rig"):
"""
Crea un'armatura vuota pronta per ricevere ossa.
Ritorna (arm_obj, armature).
"""
bpy.ops.object.armature_add(enter_editmode=True,
align='WORLD', location=(0,0,0))
arm_obj = bpy.context.active_object
arm_obj.name = name
armature = arm_obj.data
armature.name = name + "_Armature"
# Rimuovi l'osso di default
armature.edit_bones.remove(armature.edit_bones[0])
return arm_obj, armature
def add_bone(armature, name, head, tail, parent=None,
connected=False, roll=0.0, layer=0):
"""
Aggiunge un osso all'armatura (deve essere in Edit Mode).
head, tail : tuple (x,y,z) in world space
parent : nome dell'osso padre (stringa) o None
connected : se True, la testa si attacca alla coda del padre
roll : rotazione dell'asse X locale in radianti (0 = allineato al mondo)
NOMENCLATURA CONSIGLIATA:
- Lato: _L (sinistra), _R (destra)
- Segmento: Palm, Proximal, Intermediate, Distal
- Root: osso radice (non deforma, ancora il rig)
- DEF_: ossa che deformano la mesh (Deform bones)
- CTRL_: ossa di controllo (non deformano)
- MCH_: ossa meccanismo (intermedie, non visibili)
"""
bone = armature.edit_bones.new(name)
bone.head = Vector(head)
bone.tail = Vector(tail)
bone.roll = roll
bone.use_deform = True
if parent:
bone.parent = armature.edit_bones[parent]
bone.use_connect = connected
return bone
# ── Esempio: dito indice ──────────────────────────────────────────────
def create_finger(armature, name, base_y, segments=None):
"""
Crea una catena di ossa per un dito.
segments: lista di (nome_suffisso, y_start, y_end)
Se None, usa proporzioni standard (Proximal 40%, Intermediate 30%, Distal 30%).
"""
if segments is None:
segments = [
("Palm", 0.0, -2.0),
("Proximal", -2.0, -4.0),
("Intermediate",-4.0, -5.5),
("Distal", -5.5, -6.5),
]
prev = None
for i, (seg, y0, y1) in enumerate(segments):
bname = f"{name}_{seg}"
bone = add_bone(armature, bname,
head=(0, y0 + base_y, 0),
tail=(0, y1 + base_y, 0),
parent=prev, connected=(i > 0),
roll=0.0) # ← ROLL = 0: asse X orizzontale
prev = bname
FK / IK — DUALITÀ
FK (Forward Kinematics): ruota ogni osso manualmente dalla radice alla punta. Preciso, prevedibile, ma lento per animazioni complesse.
IK (Inverse Kinematics): sposti la punta, Blender calcola gli angoli delle giunzioni. Rapido per posare, ma meno preciso. Richiede un Pole Target per evitare flipping del ginocchio/gomito.
def setup_ik(arm_obj, tip_bone_name, chain_count=3,
pole_target=None, pole_bone=None, pole_angle=0):
"""
Aggiunge un vincolo IK all'osso 'tip_bone_name'.
Deve essere chiamato in POSE mode.
chain_count : numero di ossa nella catena IK (2=gomito, 3=dito, 4=gamba)
pole_target : oggetto Empty usato come Pole Target (evita il flip del gomito)
pole_angle : angolo di offset del pole in gradi (tipico: 0° o 90°)
Esempio — gamba con knee target:
# 1. Crea Empty per il knee
bpy.ops.object.empty_add(location=(0, -2, 0.5))
knee = bpy.context.active_object
knee.name = "Knee_Target"
# 2. Attiva pose mode sull'armatura
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
# 3. Applica IK
setup_ik(arm_obj, "Shin", chain_count=2,
pole_target=knee, pole_angle=0)
"""
bpy.context.view_layer.objects.active = arm_obj
if bpy.context.mode != 'POSE':
bpy.ops.object.mode_set(mode='POSE')
pbone = arm_obj.pose.bones[tip_bone_name]
ik = pbone.constraints.new('IK')
ik.chain_count = chain_count
if pole_target:
ik.pole_target = pole_target
ik.pole_subtarget = pole_bone or ""
ik.pole_angle = math.radians(pole_angle)
return ik
def setup_ik_target(arm_obj, tip_bone_name, target_obj=None, chain_count=3):
"""
Alternativa: usa un Empty come target IK (la punta segue l'Empty).
Crea automaticamente l'Empty se target_obj è None.
"""
if target_obj is None:
tip_world = arm_obj.pose.bones[tip_bone_name].tail
bpy.ops.object.empty_add(location=arm_obj.matrix_world @ tip_world)
target_obj = bpy.context.active_object
target_obj.name = f"IK_{tip_bone_name}_Target"
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
pbone = arm_obj.pose.bones[tip_bone_name]
ik = pbone.constraints.new('IK')
ik.target = target_obj
ik.chain_count = chain_count
return ik, target_obj
def toggle_ik_fk(arm_obj, bone_names, use_ik=True):
"""
Commuta tra IK e FK per un gruppo di ossa.
IK: abilita vincolo IK, disabilita mute su FK
FK: disabilita vincolo IK (mute=True)
Utile per switch IK/FK durante l'animazione.
"""
bpy.context.view_layer.objects.active = arm_obj
bpy.ops.object.mode_set(mode='POSE')
for bn in bone_names:
for c in arm_obj.pose.bones[bn].constraints:
if c.type == 'IK':
c.mute = not use_ik
WEIGHT PAINTING ALGORITMICO
Il weight painting procedurale assegna pesi ai vertex group in base alla distanza da punti di controllo. Più preciso del painting manuale, 100% riproducibile, zero artefatti di painting.
Smoothstep interpolator
def smoothstep(x):
"""Interpolazione cubica [0,1] → [0,1]. Derivata zero agli estremi."""
x = max(0.0, min(1.0, x))
return x * x * (3 - 2 * x)
def smootherstep(x):
"""Quintica — transizione ancora più morbida. Per blend tra giunzioni."""
x = max(0.0, min(1.0, x))
return x * x * x * (x * (x * 6 - 15) + 10)
Weight painting per coordinata (catene lineari)
def weight_paint_by_coord(mesh_obj, bone_ranges, coord_axis='Y',
blend_zone=0.3):
"""
Assegna pesi ai vertex group in base alla coordinata di ogni vertice.
Ideale per catene lineari (dito, colonna, arto).
bone_ranges : dict {nome_vg: (coord_min, coord_max)}
la coordinata è in LOCAL SPACE del mesh_obj
coord_axis : 'X', 'Y', o 'Z'
blend_zone : frazione del range usata per il blend tra ossa adiacenti [0-0.5]
Esempio (dito lungo Y, da 0 a -6.5):
weight_paint_by_coord(skin, {
"Palm": ( 0.0, -2.0),
"Proximal": (-2.0, -4.0),
"Intermediate":(-4.0, -5.5),
"Distal": (-5.5, -6.5),
}, coord_axis='Y', blend_zone=0.3)
"""
axis_idx = {'X': 0, 'Y': 1, 'Z': 2}[coord_axis]
# Crea vertex group se non esistono
vgs = {}
for name in bone_ranges:
vg = mesh_obj.vertex_groups.get(name)
if vg is None:
vg = mesh_obj.vertex_groups.new(name=name)
vg.add(list(range(len(mesh_obj.data.vertices))), 0.0, 'REPLACE')
vgs[name] = vg
names = list(bone_ranges.keys())
ranges = list(bone_ranges.values())
for v in mesh_obj.data.vertices:
coord = v.co[axis_idx]
for i, (name, (cmin, cmax)) in enumerate(zip(names, ranges)):
span = cmax - cmin if cmax != cmin else 1e-6
blend = abs(span) * blend_zone
if min(cmin, cmax) 0 and abs(blend) > 1e-8:
fade_in = abs((coord - cmin) / blend)
if fade_in 1e-8:
fade_out = abs((cmax - coord) / blend)
if fade_out 0:
vgs[name].add([v.index], w, 'ADD')
### Weight painting per distanza da punto 3D
def weight_paint_by_distance(mesh_obj, vg_name, center_local,
max_dist, falloff='SMOOTH'):
"""
Assegna pesi in base alla distanza da un punto 3D in spazio locale.
Ideale per aree di influenza circolari (nocca, spalla, muscolatura).
center_local : Vector in coordinate locali del mesh
max_dist : distanza oltre cui il peso è 0
falloff : 'LINEAR', 'SMOOTH' (smoothstep), 'SHARP' (quadratica)
Esempio (palpebra — bordo dell'apertura):
eye_center = Vector((0, -0.5, 0.35))
weight_paint_by_distance(eyelid, "UpperLid", eye_center,
max_dist=0.9, falloff='SMOOTH')
"""
vg = mesh_obj.vertex_groups.get(vg_name)
if vg is None:
vg = mesh_obj.vertex_groups.new(name=vg_name)
center = Vector(center_local)
for v in mesh_obj.data.vertices:
dist = (v.co - center).length
t = max(0.0, 1.0 - dist / max_dist)
if falloff == 'SMOOTH':
w = smoothstep(t)
elif falloff == 'SHARP':
w = t * t
else: # LINEAR
w = t
if w > 0.001:
vg.add([v.index], w, 'REPLACE')
SHAPE KEYS + DRIVER — Biomeccanica Procedurale
I Shape Key + Driver creano deformazioni reattive: la nocca si solleva quando il dito si piega, l'occhio si gonfia quando si chiude, il muscolo si contrae quando l'osso ruota.
def add_shape_key_with_driver(mesh_obj, key_name, driver_bone,
arm_obj, transform='ROT_X',
expression="abs(rot) / 1.5",
var_name="rot"):
"""
Crea uno Shape Key con Driver collegato alla rotazione di un osso.
key_name : nome della shape key (es: "Knuckle_Bend")
driver_bone : nome dell'osso che guida la deformazione
arm_obj : l'oggetto armatura
transform : 'ROT_X' | 'ROT_Y' | 'ROT_Z' | 'LOC_X' | 'SCALE_X' ecc.
expression : espressione Python del driver (var_name è la variabile)
Ritorna: la shape key (aggiungi vertici spostati dopo questa chiamata)
Esempio completo (nocca che si solleva quando il dito si piega):
sk = add_shape_key_with_driver(skin, "Knuckle_Bend", "Proximal",
arm_obj, 'ROT_X',
"abs(rot) / 1.5")
# Poi sposta i vertici della nocca nella sk.data:
for i, v in enumerate(skin.data.vertices):
if is_near_knuckle(v.co):
influence = knuckle_weight(v.co)
sk.data[i].co.z += 0.3 * influence
"""
# Assicurati che esista la Basis key
if not mesh_obj.data.shape_keys:
mesh_obj.shape_key_add(name="Basis", from_mix=False)
sk = mesh_obj.shape_key_add(name=key_name, from_mix=False)
sk.value = 0.0
# Aggiunge il Driver
fc = sk.driver_add("value")
driver = fc.driver
driver.type = 'SCRIPTED'
var = driver.variables.new()
var.name = var_name
var.type = 'TRANSFORMS'
t = var.targets[0]
t.id = arm_obj
t.bone_target = driver_bone
t.transform_type = transform
t.transform_space = 'LOCAL_SPACE'
driver.expression = expression
return sk
def deform_knuckle(mesh_obj, joint_y, influence_radius=0.8,
lift_z=0.3, compress_z=0.4):
"""
Applica la deformazione della nocca a una shape key già creata.
Chiama questa DOPO add_shape_key_with_driver.
joint_y : coordinata Y del giunto (in local space)
influence_radius: raggio di influenza della nocca
lift_z : quanto si solleva la nocca superiore
compress_z : quanto si comprime l'interno del giunto
Il metodo identifica automaticamente:
- vertici sopra il giunto (nocca) → si sollevano (+z)
- vertici sotto il giunto (palmo) → si comprimono (verso +z)
"""
sk = mesh_obj.data.shape_keys.key_blocks[-1] # l'ultima shape key
for i, v in enumerate(mesh_obj.data.vertices):
dist = abs(v.co.y - joint_y)
if dist 0: # parte superiore → nocca
sk.data[i].co.z += lift_z * influence
sk.data[i].co.y += 0.2 * influence
else: # parte inferiore → compressione
sk.data[i].co.z += compress_z * influence
SOCKET SYSTEM — Collegare Oggetti a Ossa
Il Socket System usa il vincolo CHILD_OF per ancorare oggetti a ossa. Permette di attaccare armi, attrezzi, oggetti a una mano/braccio.
def socket_attach(mesh_obj, arm_obj, bone_name,
local_offset=(0,0,0), local_rotation=(0,0,0)):
"""
Attacca mesh_obj all'osso bone_name tramite vincolo CHILD_OF.
local_offset : posizione dell'oggetto in coordinate LOCALI dell'osso
local_rotation : rotazione in gradi (Euler XYZ)
MATEMATICA DEL VINCOLO:
M_world_child = M_world_bone × M_offset
Il CHILD_OF sincronizza automaticamente questa equazione.
Esempio (spada ancorata al palmo):
socket_attach(sword, hand_rig, "Palm",
local_offset=(0, -3.5, -0.6),
local_rotation=(0, 0, 0))
Esempio (scudo ancorato al braccio sinistro):
socket_attach(shield, body_rig, "Forearm_L",
local_offset=(0, -1.0, 0.1),
local_rotation=(0, 90, 0))
"""
c = mesh_obj.constraints.new('CHILD_OF')
c.target = arm_obj
c.subtarget = bone_name
# Offset locale rispetto all'osso
mesh_obj.location = Vector(local_offset)
mesh_obj.rotation_euler = tuple(math.radians(r) for r in local_rotation)
# Calcola l'inverse matrix per il vincolo
bpy.context.view_layer.objects.active = mesh_obj
bpy.ops.constraint.childof_set_inverse(
constraint=c.name, owner='OBJECT')
return c
def socket_detach(mesh_obj, bone_name=None):
"""Rimuove tutti i vincoli CHILD_OF (o solo quello verso bone_name)."""
for c in list(mesh_obj.constraints):
if c.type == 'CHILD_OF':
if bone_name is None or c.subtarget == bone_name:
mesh_obj.constraints.remove(c)
PELLE BIOMECCANICA — Shrinkwrap + Solidify
Tecnica per creare pelle che avvolge uno scheletro curvo:
- Crea una mesh di partenza dalla forma generale (sfera, cili
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: MAX-786
- Source: MAX-786/claude-3d-harness
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.