Blenderには他のオブジェクトやボーンの移動量に合わせて他のオブジェクトの設定値を変更するドライバー という機能があります。
例えばボーンのポーズモードで トランスフォームのX位置の数値で右クリックして
メニューの中にある「新規ドライバーとしてコピー」を押して
シェイプキーの値の欄を右クリックして「ドライバーを貼り付け」を実行すると
ボーンのポーズモードでのX移動に合わせてシェイプキーの値が変化するドライバが作成できます
シェイプキーはキャラクターの表情付け等に便利で こうやってドライバで制御するようにすればボーンアニメーションと同時に扱えます
ですが全てを設定するのはとても煩雑です
ボーンのコントローラーを一括して作成するスクリプトを作成してみました
メッシュを選択した状態で実行するとメッシュに設定されたシェイプキーから
シェイプキーを制御するドライバを設定したボーンとボーン名と目盛りのカスタムシェイプを作成します。
import bpy
import zlib
import base64
import json
# 文字メッシュのデータ
mesh_dic_zip = {
"A":"eJyLjo421FEwjNVRANFmINoUQcPETaB8MG2ko2AOok1ANIgBV2Gso2AKos2gOpFUgsXNoSYD+QZQE41iY2MBnaQdVw==",
"C":"eJxNjMkNADAIw1ZhAB4cPYZB7L9GiZRHX0YmclW5ymkVMMBQueAiN//jnd7po3GU0czCwGRplsny/ksH5e4HuOEbkg==",
"B":"eJxFjcENACEIBFuhAB4qKloLof82jk023mvYsAwRsVRGqsRU6WBlY57MC+zMnb2is+fs7cSATcPQaB402m+0d1F0fjg0XZVD8wU3PmZ+zvsl+g==",
"D":"eJwtzMsNAEEIAtBWKIDDfHS2GEP/bawknh5KtKqC+ERUEs/umdttY+z+yMHNcljetIe49hIxFzkfUtIPbIgWZw==",
"E":"eJyLjo421VEwj9VRiDZE0CYg2gRKA/mGINoURIMY0QZQEaCMEYg20lEwhtIwnaaxsbEAwWkUrg==",
"F":"eJyLjo421VEwj9VRiDZE0CYg2gRKA/mGsSBGtAGIBRExAtFGOgrGUNokNjYWAJgRET8=",
"G":"eJxNzcsNwDAIA9BVGIBDfiTtLIj91yiOrKSnhwCDuzeVFSo+aFWZtMGcV863xn46YKeGHAokC4pyL/T/Zl6yk0gnPy+VB774FPEBjiEifg==",
"H":"eJyLjo421FEwjNVRANEmUNocRJtC+aYIvmEsiAFSYQBiGCF0GoNoEyhtrKNgGhsbCwDATBSu",
"I":"eJyLjo420lEwj9VRiDaG0iYIviGUD6aNQDSIEW2oo2AAFwHSQL4xVKUxVKdpbGwsAMHEFK4=",
"J":"eJyLjo421FEwjNVRiDbQUTAC0cZQvgmUD6TNoeLmsSAGSIcBsg4jHQVjqAoTqA7T2NhYAL4CFKg=",
"M":"eJyLjo421VEwjNVRANHmINpYR8EURBtC+YYgeRAj2gCqEihiBKKNdBSMoTpMYmNjAZgXEUE=",
"K":"eJyLjo421FEwj9VRANEmUNoQRJtCxYG0EZQ2jAUxog2gKgyhMkDaGNkEEx0F09jYWADAOBSr",
"L":"eJyLjo421FEwj9VRANGGINoESpvqKBjFghjRBlARQ5AIkDbSUTCOjY0FAKPlDcw=",
"N":"eJyLjo421VEwj9VRANGGINoQyjcE8UEMEMsAKmIEoo10FIxjY2MBpVcN0g==",
"O":"eJxFzcsNACAIA9BVGICDqOgwDfuvoTVNPD1C+QCYbrvc0GW6LdnpzUP5M9QPzrFA+xONjr85dGnS5ZZ060PwY9UBm+gdTQ==",
"P":"eJxNzLsNADEMAtBVGIAiHycZxvL+ayScKK56IAtnZidOERn29XAP9y4XsewuBV2aQiOGHMS033L+F8+tT1UX6QoZ3A==",
"Q":"eJwtjckNADAIw1ZhAB49oHQXxP5rlKh5OUEGMnOq7FLJpbLA7oc9QCOdc6dv9LvPQsiB9DcHuGkYN9o08Kg4GLzYvPwcVfUAhbggxg==",
"R":"eJxFzcENACAIA8BVGICHiIq7EPZfQ5s04XUlgZKZpmKlAhcNuDhvlU0nNXq4F4WAy4Ewu9Ghs8m7qS+/oXLhxYeqB4VjIMc=",
"3":"eJxNjrsNADEIxVZhAIqQf2ZB7L/GhcjFVUY8MLh7V9mh4hUOlQVn8uYj2eDtd9jIK/uPRt/Sk4UXFWOiYKpstr9xwsXlzSeHz+yKDmqziPgAnFUpzg==",
"5":"eJxFzdkNACAIA9BVGIAP8d6lYf81tEmNX4+UC8Bw2+mG+E7a5e0P2ZRXWmUwZ4HiFkrexNtoutDp1MWlD9ttZeYBjNsfFA==",
"X":"eJw1jMsNADAIQldxAA9V+xnGsP8aLQk9PVCgu8Mt4EYWWW6LXPLvfuQ/AxT8DIrUQqrxOJVMLW4AFyA5GBw=",
"Y":"eJyLjo421lEwjNVRANGmINpUR8EcRBuCaBADxDIAMYygKoF849jYWAClSw3V",
"0":"eJxdjMkJADEMA1tRAX7EzrW9GPffRiIQBPY1kscoM6ehlyGHIcjbt/pHurzrHvoLegaaxtCe+S8O9UUug5PbMMnO5aoDjPIfCw==",
"6":"eJxNzbkNADAIA8BVGIAiQL5dEPuvESy5SHXIRpCZU2WVShpdKpMG7N6hU2PebhgqtzAgGdwcbJwX4r+8+ak1eOhVOVX1AITgIL4=",
"Z":"eJyLjo421FEwj9VRiDaF0kC+IYg2A9EgBkjEACpjBKKNdBSMY2NjAaVIDdM=",
"V":"eJyLjo421VEwj9VRANEmINpYR8EQRBtC+YYgeRAj2gAhYwSijXQUjKE6TGJjYwGYgxFD",
"U":"eJwtjMkJADAMw1bxAHnkaOgwIfuv0Rr8kkDGM9OGXAN5yWMIMsVQD3YKzSmu8pdFluHooXf3AcBFFKs=",
"W":"eJyLjo421VEwj9VRANGGINpYR8EYRBtC+YYgeRAj2gAhYgSijaAqgTpMYmNjAZerET8=",
"T":"eJyLjo421VEwj9VRiDZG0IYg2hDEBzGiDRAiRlDaODY2FgCmIQ3X",
"S":"eJxFjcENwDAIA1dhAB6BhqSZBbH/Go0jR32dDejIzFDxUsmuYuDuD3sHnTSVIAfnk3eT3bj3QkBqCI0f/DdfBj8e46DpJdc1bsGqqg/uNiYZ",
"8":"eJxFjssRwCAIRFuhAA4uxl8tDv23ETDr5LS8EZ67924q1VX2o2KZwQ+5kXumkUEG98B7433wIE96Jj3Dc8iNkkPhhdFQabyN+t/gGAdNU2VlLhWcH8utFGYcNcKJ7zGsMBZBdfcXino1Mw==",
"9":"eJxdjcsNACEIRFuhAA6Kom4thP7bWCaZg/H0yHyYiDAVT5VwsqsscoPlH3CSTr04waFizFniiKbSqbQ7UR/G3VzP4mbvcOHDYuYPrLUkQw==",
"1":"eJyLjo421FEwj9VRiDbSUbCA0kYg2hBKG4NoEAMkYoAsA1RpDKJNwCpiAZbLETo=",
"m":"eJyLjo421VEwitVRANGmINpER8EMRBtD+UYIvjGINoTyDUH6QIxoAx0FQ7gIRIcxVIcJskkwk810FMxjY2MBuu0blw==",
"n":"eJxFyLENACAMA8FVPIALSALLRN5/DXAD1b2+uxcRIuyyRWw7vyFHD2K+cw0ibRIl6QCYSBFC",
"c":"eJwtjcERACEMAluhAB4azV0xmfTfhuLwWiYsk6oK4muitjmJNJd4+3D/mL6nPAWZQ2HYCBuL2F6mF+/DT8zuPrjcG5A=",
"e":"eJxFjcsNwDAIQ1dhAA4JhXxmQey/RnFlqadngw2ZOVWiVNJUFtjewSDbP9wb6GRwHuw7+hBoDIjBpP0XnMlg8/u4VDZ4mGte8KrMqnoBpT0kOA==",
"b":"eJyLjo421FGwiNVRANGmINoESgP5RlA+mDbVUTCG0iaxIEa0gY6CIbJKQ6gKY5AKJJOAOsxAtBlIXWwsAOezGdo=",
"d":"eJxFjMENACAIA1dhAB6CYtyFdP81tEkTX1cart0dbhNu5CLTLcn6LPWl+4CBxmAY+kwtTS0tGY9bZgC45nQZ1w==",
"g":"eJxFjcsJQEEIA1uxAA+r7q8Xsf821kAe7zQJDElmusoulTSVBS72poNTxdgDdLL9SVoh5KDZxgCDC0HzW9h8Ov/z4dOtqgeDHSC5",
"a":"eJxVjbkNwEAMw1bRACrOvi+7GN5/jUSAmlQ0QEKuqk3MJsQlLiLFNMM+7b9uuzviFHXUIMLlsEmXvw/hhfDCIa54iae7X4S0IMA=",
"f":"eJyLjo420lEwi9VRiDZG0EZQ2hxEm0LFTXQULKB8i1gQI9pQR8EAxDCE6gDSxlCVhlATTOEmxMYCACZJGDE=",
"t":"eJyLjo420lEwi9VRiDZG0MZQ2gJEm0LFTXQUjGJBjGhDHQUDEMMQJAKhjaEqDEE00ETT2NhYAMMmFLQ=",
"j":"eJyLjo421lEwjNVRiDaC0iY6CkZQ2gxEA+XNQbQhSBzEiDZA6DCA0sZQlSZQlaaxsbEAvvwUpg==",
"k":"eJyLjo420lGwiNVRANEmUNoIRJvqKJhBaaNYECPaQEfBEMQwhKoA0sZQ2iQ2NhYAmBERQA==",
"l":"eJyLjo421lGwiNVRANHGINpER8EoFsSINtBRMAQxDMEisQDpiQpo",
"o":"eJwtjccNADAIA1dhAB4pQIax2H+NYMmvw8gFwHa77YbjdsjRKV1kiKl/yh/088By20os8qop5JxEkKWGp8ZZet39AZnYHU0=",
"p":"eJxNjcsNADAIQldxAA/96jDE/deoNjTp6QkBBNBVVqhgqcxiaiM7/au3yiZvflS+DjiT6bRi41I2x98wLrwPmfOIOJqaHUk=",
"q":"eJxFzMkNADEIA8BWXACPBHIUg+i/jcSRV/sahGxnphuiDNkNg87fLhd1eXOTBns8+Gk8tsGVDPmWhhrf0tJyM+yqOptZHUw=",
"r":"eJyLjo420lEwjdVRANFmUNoIRBtD+SYgGsSINtRRMAAxDKAqgLQxVKVJbGwsAJezEUA=",
"h":"eJyLjo421VEwitVRANGmINpER8EMRBtDaUOouCFUHZC2iAUxog10FAyRZYx0FIyhOk2gJiFMjI0FACN3GCY=",
"s":"eJw1jMERACEIA1uhAB6KyhWTof82Tp31FTJZVlJ3i3LTJJfboM+TQW5u0ZM96xxqbh0iIJ5hYLyGxPDdz/oBuAwblQ==",
"v":"eJyLjo421VEwjdVRANFmINpYR8EIRBtCxQ1B4iAGiGUAYhhAVRjpKBhDdZjExsYCAJidEUM=",
"u":"eJyLjo421VEwitVRANFmINoIyjfUUTCG0maxIAaIZQBiGEBVGEFVGOsomMTGxgIAlzYRPQ==",
"w":"eJyLjo420VEwitVRiDbVUTAD0cY6CiYg2ggqbggSBzFALAMQwwAqA1RhDNcRGwsAl5URPw==",
"y":"eJxNjMkNADAIw1ZhAB4tvYaJsv8aBSmV+jIgbADhNuiG7jaLS3tyi1Gcbl1/j5s1oOmSpVYcMkLFNNdfTPOQvLbdG4k=",
"x":"eJyLjo421VEwitVRiDbWUTAB0YZQPpA2A9GmIBrEAIkYoKkwBtEmOgqGsbGxAJcwET0=",
"z":"eJyLjo421FEwi9VRiDaF0kC+EZRvFAtigEQMkGWMdBSMY2NjAaUHDdI=",
"i":"eJyLjo421lEwitVRANFmINoIRIMY0QY6CoYghiFIRWwsAOklCmc=",
"4":"eJyLjo421FEwjdVRANEmINpUR8ECSsP4RrEgRrSBjoIhiGGko2AAoo1BMhCdxlC+SWxsLAAnMBL4",
"2":"eJwtjMkRACAIA1uhAB6CovbC0H8bEievHJDNzKVySyVD5UCd2Zi739D+C/YO3VAYNANm8NKEyYWRsLj4hANi1QO/+xuk",
"7":"eJyLjo421FGwiNVRANHmINoUyjfWUTCB0kaxIEa0gY6CIYhhpKNgAKWN4SpjYwGZLBFG",
".":"eJyLjo421lEwidVRiDbRUTCOBTGiDXQUDGNjYwFkPQb8",
",":"eJyLjo421lEwidVRiDbSUTCKBTGiDXQUDGNjYwFkDQb5",
"/":"eJyLjo421FEwitVRiDbVUbCIBTGiDXQUDGNjYwFkNgb+",
"?":"eJxNjcsNwDAIQ1fxABxKAvnMgth/jYbIlXp6yMZ2RKhgpiCaYBWdHNQPB3UvdoGRrWgCZb5nHfFQUX5c55c0Njmbv6XJ5cOdmS+XviKV",
"&":"eJxFjssNwCAMQ1fxAD6Q8CnMErH/Gm2QUU+PBOs5EeHEs4moxBRHsmlvREt2oiYH4WJX7swuWubyEYUw/RSZb/IaXRziaZwyL11kn2ipysrfsV+YcynA",
"'":"eJyLjo420VGwiNVRiDbWUTCLBTGiDXQUDGNjYwFkxwcD",
"+":"eJyLjo421lEwjdVRANHGUNocRJtCxQ1BNIgRbaCjYAhiGOkoGIBoA6gOIG0SGxsLAJhLET8=",
"-":"eJyLjo421VEwjdVRiDYE0SBGtIGOgmFsbCwAZG4G/g==",
"*":"eJxFy8ENgFAIA9BVGIADiIi7kO6/hjZp8k+vtGF3y63hRksObfUpW3uefXQXGDbckuFyCxr6+L1ly0eOfAF8iRke+Q==",
"Null":"eJyLjo421VGwiNVRANFGINoQyjcE8UGMaAMdBUMQwwCqwkhHwRhEG4PEY2MBGWwPiQ==",
"_":"eJyLjo421VEwitVRiDYE0SAGiGUQGxsLAGQFBvg=",
"!":"eJyLjo420lEwjtVRiDbWUTAC0SY6CoZQvkUsiBFtqKNgAGIYIWQMY2NjAaRNDc8=",
"#":"eJxNTcERwDAIWsUBfKg1aTqL5/5rNObo2RcgAhHhTDOZCr3QWq/CAW3tn7v23aAdOUPOs0gIkxYROBeT4FPQMP7Ldzec3IK/9YMllY9oZr7nAStl",
")":"eJyLjo421lGwiNVRiDbRUTCH0sYgGihuFAtiRBvqKBiAGIYgEbhMLACmRA3Y",
"(":"eJyLjo420VGwiNVRiDbWUTCH0sYgGihuFAtiRBvqKBiAGIYgEYgKoEwsAKZEDdg=",
"@":"eJxdjssNwDAIQ1dhAA51/pklYv81CshRpZ5ecMhzzjldpZlKcAYb58v6zYNzsqj037vFPAnmzsq8cL9YHM6jAt48NBeaKjcvs3HQOPkjsGmp7OB2X4oQ5lTDd5BSeAtSB+9BM7MXduc3Ig==",
"<":"eJyLjo420VEwj9VRiDbUUTAB0UC+YSyIARIxADGMwCKxAOlYCmY=",
">":"eJyLjo420lGwiNVRiDbVUTAF0UC+USyIEW2oo2AAFTGMjY0FAOmyCmk=",
"^":"eJyLjo421FEwi9VRiDbWUbAA0aYgPogBkjEAMYx0FAxjY2MB6fgKbg==",
}
context = bpy.context
def shepekey_control_bones(key_blocks):
# シェイプキー名でコントロールボーンを作成
# 各ボーン間の距離
shape_offset = 0.1
# ボーンの長さ
bone_length = 0.05
# 一覧の開始位置
start_pos = [1.0 ,0 ,1.5]
# アーマチュアの作成
armature_data = bpy.data.armatures.new("shape_contrl")
armature_obj = bpy.data.objects.new('shape_contrl', armature_data)
context.scene.collection.objects.link(armature_obj)
bpy.context.view_layer.objects.active = armature_obj
bpy.ops.object.mode_set(mode = 'EDIT')
edit_bones = armature_data.edit_bones
contrl_bones_name = []
for i,key in enumerate(key_blocks):
if key.name == "Basis": continue
# コントロールボーン
bone_1 = edit_bones.new( key.name )
bone_1.head = [start_pos[0], start_pos[1], start_pos[2] -i *shape_offset +bone_length]
bone_1.tail = [start_pos[0], start_pos[1], start_pos[2] -i *shape_offset]
bone_1.use_deform = False
contrl_bones_name.append( key.name )
# コントロールのシェイプのボーン
bone_2 = edit_bones.new( "WG_" +key.name )
bone_2.head = [start_pos[0], start_pos[1], start_pos[2] -i *shape_offset]
bone_2.tail = [start_pos[0], start_pos[1], start_pos[2] -i *shape_offset +bone_length]
bone_2.use_deform = False
bpy.ops.object.mode_set(mode = 'OBJECT')
for bone_name in contrl_bones_name:
# シェイプの設定
wg_name = "WG_" +bone_name
wg_bone = armature_obj.pose.bones[wg_name]
wg_obj = criate_scaleshape(bone_name)
# シェイプボーンの移動をロック
wg_bone.custom_shape = wg_obj
wg_bone.lock_location = [True, True, True]
wg_bone.lock_rotation = [True, True, True]
wg_bone.lock_scale = [True, True, True]
# コントロールボーンの移動をロック
ct_bone = armature_obj.pose.bones[ bone_name ]
ct_bone.lock_location = [False, True, True]
ct_bone.lock_rotation = [True, True, True]
ct_bone.lock_scale = [True, True, True]
return(armature_obj)
def unzip_mesh_str(text):
# 圧縮メッシュデータの展開
bin = base64.b64decode(text)
mesh_str = zlib.decompress(bin).decode()
return json.loads(mesh_str)
def criate_scaleshape(str):
# シェイプボーン用の目盛り用シェイプデータの作成
mesh = bpy.data.meshes.new(str)
obj = bpy.data.objects.new(str, mesh)
# 文字の大きさ
scale = 0.15
# スケール部分のデータ
vertices = [[-0.1, 0.2, 0.0], [-0.1, 0.5, 0.0], [0.0, 0.5, 0.0], [0.0, 0.2, 0.0],
[5.0, 0.2, 0.0], [5.0, 0.5, 0.0], [5.1, 0.5, 0.0], [5.1, 0.2, 0.0], [2.5, 0.1, 0.0], [2.5, 0.4, 0.0]]
edges = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [8, 9]]
vertex_count = 10
# シェイプキー名の文字列データの作成
for i,s in enumerate(str):
if s in mesh_dic_zip:
(v_list, e_list) = unzip_mesh_str(mesh_dic_zip[s])
else:
(v_list, e_list) = unzip_mesh_str(mesh_dic["Null"])
offset = 5 *i *scale +5.4
vertices += [(float(v[0]) *scale + offset, float(v[1] *scale), 0) for v in v_list]
edges += [[e[0] +vertex_count, e[1] +vertex_count] for e in e_list]
vertex_count += len(v_list)
# メッシュの作成
mesh.from_pydata(vertices, edges,[])
mesh.update()
return(obj)
def set_bone_driver(key_blocks, armature_obj):
# ボーンとシェイプキーの対応付け
for key in key_blocks:
if key.name in armature_obj.data.bones.keys():
prop = key.driver_add("value")
#スクリプトタイプのドライバ
prop.driver.type = 'SCRIPTED'
#ドライバ変数の作成
var = prop.driver.variables.new()
var.name = 'Var'
var.type = 'TRANSFORMS'
# ドライバのターゲットの設定
var.targets[0].id = armature_obj
var.targets[0].bone_target = key.name
# ポーズモードのX軸移動で移動
var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'LOCAL_SPACE'
# ドライバー式
prop.driver.expression = 'Var*4'
def set_bone_limit_loc(key_blocks, armature_obj):
# コントロールボーンに位置制限のコンストレイントの設定
for key in key_blocks:
if key.name in armature_obj.pose.bones.keys():
pose_bone = armature_obj.pose.bones[ key.name ]
constraint = pose_bone.constraints.new('LIMIT_LOCATION')
constraint.use_min_x = True
constraint.min_x = 0.0
constraint.use_max_x = True
constraint.max_x = 0.25
constraint.use_transform_limit = True
constraint.owner_space = 'LOCAL'
if context.active_object.type == 'MESH':
mesh = context.active_object.data
key_blocks = mesh.shape_keys.key_blocks
# アーマチュアの作成
armature_obj = shepekey_control_bones(key_blocks)
# ドライバーの設定
set_bone_driver(key_blocks, armature_obj)
# コントロールボーンに位置制限のコンストレイントの設定
set_bone_limit_loc(key_blocks, armature_obj)
スクリプトでは新しいアーマチュアオブジェクトで作成しますが
既存のボーンに統合しても ドライバの設定は引き継がれるようです
既存のドライバがある場合やボーンを作り直したい場合の処理を作成していませので
作り直す場合には既存のドライバは削除してください
ドライバエディタでKeyの項目名を選択して削除をすると 選択した項目名のドライバを削除できます