Blender 自動化スクリプトで定形オブジェクト作成で楽をする、円柱とボーンを追加するWiggle2ポーズモード
BlenderはPythonスクリプトを実行して、命令やオブジェクトを作成できる。これで定形オブジェクト作成で楽をする。自動化すると楽になる。
次のスクリプトは、1個の円柱を作成して、指定された数の親子ボーンを追加するまでのスクリプト。実行環境はWindows10:Blender4.2
内部メモ、C:\Users\mased\Documents\Blender\blender\model\テスト、研究モデル\しっぽ揺れアニメ\Wiggle実験尻尾揺れ、2024年10月9日、ファイル名make_obj_automticWeight_006.py ダウンロードファイルはなし(ウィルス感染とか内容の更新とかできないので、、Githubという方法もあるが仕組みが未だに理解できないので使えない老衰脳)
Wiggl2のStiffは数値が高いほど固く、数値が低いほど柔い。なので、Stiff:200が柔らかく、800とかだと固い。(訂正) 数値が高いほど揺れやすい。低いほど固い動きになる。だからTailの尻尾の先にStiff800以上にするとすごく揺れまくる。これは実際にアニメーションして動きをチェックして確認が必要
ブログ内関連リンク、マテリアル設定スクリプト記事
実行文はinfo情報から取得しているので、無駄なパラメータも多いかもしれん。エラー処理は殆ど無いので、極端な数値は入れないようにしたほうがよい。
For文のrange(N)は、「0から始まってN-1」まで。個数はN個。なんとなく「0からN」までか「1からN」までのように思うが実際は違うので勘違いしないように。
import os import bpy import math import copy import time #2024/10/9 #make obj 円柱作成 Blender text ここでは日本語の入力はできないので、他で入力してコピペして if __name__ == "__main__": #main GO #var gDepth=6# cylinder hight 円柱高さ6mに対して、ボーンの最大数は11か12個くらい gCountBones=6# bone count over 2 number.limit count is [11 or 12]. << by gDepth:6 # 3Dカーソルの位置を元に戻す bpy.context.scene.cursor.location=(0.0,0.0,0.0) #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2) # location=(0, 0, 3)#z is 3m , Depth is 6m, its half size. bpy.ops.mesh.primitive_cylinder_add(vertices=16,radius=0.5, depth=gDepth, enter_editmode=False, align='WORLD', location=(0, 0, gDepth/2), scale=(1, 1, 1)) #CTR+R bunkatsu 16 cuts bpy.ops.object.editmode_toggle() bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut={"number_cuts":16, "smoothness":0, "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":15, "mesh_select_mode_init":(True, False, False)}, TRANSFORM_OT_edge_slide={"value":0, "single_side":False, "use_even":False, "flipped":False, "use_clamp":True, "mirror":True, "snap":False, "snap_elements":{'INCREMENT'}, "use_snap_project":False, "snap_target":'CLOSEST', "use_snap_self":True, "use_snap_edit":True, "use_snap_nonedit":True, "use_snap_selectable":False, "snap_point":(0, 0, 0), "correct_uv":True, "release_confirm":False, "use_accurate":False}) bpy.ops.object.editmode_toggle()#from edit to OBJ mode # next is bone add . #bpy.context.space_data.shading.type = 'WIREFRAME' #add bone 1 bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, gDepth/gCountBones)) #6/4=1.5 # Edit mode bpy.ops.object.editmode_toggle() #add bone2 to (gCountBones-1) #E key EXTRUDE add child bone if gCountBones < 2:#最低でも2個は作成する gCountBones=2 #range(num) is 0 to num-1. exp: num=9 then 012345678 , its 9 counts. not exist [number9] . for i in range(gCountBones):# range() is from 0 to (gcountBones-1), total count is [gCountBOnes]. if i >= gCountBones-1: break else: bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},\ TRANSFORM_OT_translate={"value":(0, 0, gDepth/gCountBones),\ "orient_type":'GLOBAL',\ "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),\ "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, True),\ "mirror":False, "use_proportional_edit":False,\ "proportional_edit_falloff":'SMOOTH',\ "proportional_size":1,\ "use_proportional_connected":False,\ "use_proportional_projected":False,\ "snap":False, "snap_elements":{'INCREMENT'},\ "use_snap_project":False, "snap_target":'CLOSEST',\ "use_snap_self":True, "use_snap_edit":True,\ "use_snap_nonedit":True, "use_snap_selectable":False,\ "snap_point":(0, 0, 0), "snap_align":False,\ "snap_normal":(0, 0, 0), "gpencil_strokes":False, \ "cursor_transform":False, "texture_space":False,\ "remove_on_cancel":False, "use_duplicated_keyframes":False,\ "view2d_edge_pan":False, "release_confirm":False,\ "use_accurate":False, "use_automerge_and_split":False}) #comment """ #bone3 """・ブログ内関連リンク
円柱、ボーンを作成する関数とウェイト設定をする関数を作成した
import os import bpy import math import copy import time #2024/10/9 #print文はBlenderのメニューバーの Window > Toggle System Consoleを実行するとDOS窓が表示される #make obj 円柱作成 Blender text ここでは日本語の入力はできないので、他で入力してコピペして #Next here 10/14 #next code is 3 obj and 3 armatureBones. so make Function objAndBones(location,gDepth,gCountBones) def funcObjAndBones(mylocation,myDepth,myCountBones): #location=(0, 0, gDepth/2) loc=mylocation #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2) #中心点をDepth6m/2 半分Z軸を上げる # location=(0, 0, 3)#z is 3m , Depth is 6m, its half size. bpy.ops.mesh.primitive_cylinder_add(vertices=16,radius=0.5, depth=myDepth, enter_editmode=False,\ align='WORLD', location=loc, scale=(1, 1, 1)) #CTR+R bunkatsu 16 cuts #Start edit mode bpy.ops.object.editmode_toggle() bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut= {"number_cuts":16, "smoothness":0,\ "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":15,\ "mesh_select_mode_init":(True, False, False)},\ TRANSFORM_OT_edge_slide={"value":0, "single_side":False,\ "use_even":False, "flipped":False, \ "use_clamp":True, "mirror":True, "snap":False, \ "snap_elements":{'INCREMENT'}, "use_snap_project":False, \ "snap_target":'CLOSEST', "use_snap_self":True, \ "use_snap_edit":True, "use_snap_nonedit":True,\ "use_snap_selectable":False, "snap_point":(0, 0, 0),\ "correct_uv":True, "release_confirm":False, "use_accurate":False}) bpy.ops.object.editmode_toggle()#from edit to OBJ mode # end edit mode #add bone 1 #ボーンのlocationのZ軸は原点に戻す必要がある。 loc=(mylocation[0],mylocation[1],mylocation[2]-myDepth/2) bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=loc, \ scale=(1, 1, myDepth/myCountBones)) #6/4=1.5 Z軸スケールがなんかうまくいってない2024/10/15 # Edit mode start bpy.ops.object.editmode_toggle() #add bone2 to (gCountBones-1) #ボーンの増加は、大きい親ボーンを分割処理してもいいのかも。試してみて。2024/10/15 #E key EXTRUDE add child bone if myCountBones < 2: myCountBones=2 #range(num) is 0 to num-1. exp: num=9 then 012345678 , its 9 counts. not exist [number9] . for i in range(myCountBones):# range() is from 0 to (gcountBones-1), total count is [gCountBOnes]. if i >= myCountBones-1: break else: bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},\ TRANSFORM_OT_translate={"value":(0, 0, myDepth/myCountBones),\ "orient_type":'GLOBAL',\ "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),\ "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, True),\ "mirror":False, "use_proportional_edit":False,\ "proportional_edit_falloff":'SMOOTH',\ "proportional_size":1,\ "use_proportional_connected":False,\ "use_proportional_projected":False,\ "snap":False, "snap_elements":{'INCREMENT'},\ "use_snap_project":False, "snap_target":'CLOSEST',\ "use_snap_self":True, "use_snap_edit":True,\ "use_snap_nonedit":True, "use_snap_selectable":False,\ "snap_point":(0, 0, 0), "snap_align":False,\ "snap_normal":(0, 0, 0), "gpencil_strokes":False, \ "cursor_transform":False, "texture_space":False,\ "remove_on_cancel":False, "use_duplicated_keyframes":False,\ "view2d_edge_pan":False, "release_confirm":False,\ "use_accurate":False, "use_automerge_and_split":False}) bpy.ops.object.editmode_toggle() #End Edit mode #end def #円柱とボーンにウェイトを設定する。オブジェクト名を指定して1組だけ設定 def funcAutomaticWeight(firstObjName,SecondObjName): #next 1個ずつ親子設定にしていく、できれば親子関係がすでに設定されていれば何もしないようにしたいが、よくわからない gFindOne="" gFindTwo="" #object mode にしないとオートウェイとが正しく動作しない print("now mode is ",bpy.context.mode) #editモードだったら、EDIT_ARMATURE のように出力される if bpy.context.mode == 'OBJECT': print("now OBJECT MODE") else: bpy.ops.object.editmode_toggle()#Objectモードに変更 #2回ループは非効率的だけど、今はこれでいく 1組のオブジェクトとボーンをウェイト設定 #Cylinder for obj in bpy.data.objects: if firstObjName in obj.name: print("find obj1") obj.select_set(True) gFindOne="ari" #Armature for obj in bpy.data.objects: if SecondObjName in obj.name: print("find obj2") obj.select_set(True) gFindTwo="ari" #できれば、すでに親子関係・ウェイトモードが設定されているかを判別したいけど、わからない if gFindOne=="ari" and gFindTwo=="ari": print("add parent do") #bone weight Automatic Weight Ctr+P オブジェクトモードで実行する必要がある bpy.ops.object.parent_set(type='ARMATURE_AUTO') #end def if __name__ == "__main__": #main GO #var gDepth=6# cylinder hight gCountBones=6# bone count over 2 number.limit count is [11 or 12]. by gDepth:6 # 3Dカーソルの位置を元に戻す bpy.context.scene.cursor.location=(0.0,0.0,0.0) #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2) testLocation=(0.0,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,8) #location,high,bonesCount funcAutomaticWeight("Cylinder","Armature") #2つめの円柱 testLocation=(1.5,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCount funcAutomaticWeight("Cylinder.001","Armature.001") #3つめ testLocation=(3.0,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,4) #location,high,bonesCount funcAutomaticWeight("Cylinder.002","Armature.002") #end for #comment """ #bone3 """
以下スクリプト
import os import bpy import math import copy import time #2024/10/9 #print文はBlenderのメニューバーの Window > Toggle System Consoleを実行するとDOS窓が表示される #make obj 円柱作成 Blender text ここでは日本語の入力はできないので、他で入力してコピペして #Next here 10/14 #next code is 3 obj and 3 armatureBones. so make Function objAndBones(location,gDepth,gCountBones) def funcObjAndBones(mylocation,myDepth,myCountBones): #location=(0, 0, gDepth/2) loc=mylocation #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2) #中心点をDepth6m/2 半分Z軸を上げる # location=(0, 0, 3)#z is 3m , Depth is 6m, its half size. bpy.ops.mesh.primitive_cylinder_add(vertices=16,radius=0.5, depth=myDepth, enter_editmode=False,\ align='WORLD', location=loc, scale=(1, 1, 1)) #CTR+R bunkatsu 16 cuts #Start edit mode bpy.ops.object.editmode_toggle() bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut= {"number_cuts":16, "smoothness":0,\ "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":15,\ "mesh_select_mode_init":(True, False, False)},\ TRANSFORM_OT_edge_slide={"value":0, "single_side":False,\ "use_even":False, "flipped":False, \ "use_clamp":True, "mirror":True, "snap":False, \ "snap_elements":{'INCREMENT'}, "use_snap_project":False, \ "snap_target":'CLOSEST', "use_snap_self":True, \ "use_snap_edit":True, "use_snap_nonedit":True,\ "use_snap_selectable":False, "snap_point":(0, 0, 0),\ "correct_uv":True, "release_confirm":False, "use_accurate":False}) bpy.ops.object.editmode_toggle()#from edit to OBJ mode # end edit mode #add bone 1 #ボーンのlocationのZ軸は原点に戻す必要がある。 loc=(mylocation[0],mylocation[1],mylocation[2]-myDepth/2) bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=loc, \ scale=(1, 1, myDepth/myCountBones)) #6/4=1.5 Z軸スケールがなんかうまくいってない2024/10/15 # Edit mode start bpy.ops.object.editmode_toggle() #add bone2 to (gCountBones-1) #ボーンの増加は、大きい親ボーンを分割処理してもいいのかも。試してみて。2024/10/15 #E key EXTRUDE add child bone if myCountBones < 2: myCountBones=2 #range(num) is 0 to num-1. exp: num=9 then 012345678 , its 9 counts. not exist [number9] . for i in range(myCountBones):# range() is from 0 to (gcountBones-1), total count is [gCountBOnes]. if i >= myCountBones-1: break else: bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},\ TRANSFORM_OT_translate={"value":(0, 0, myDepth/myCountBones),\ "orient_type":'GLOBAL',\ "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),\ "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, True),\ "mirror":False, "use_proportional_edit":False,\ "proportional_edit_falloff":'SMOOTH',\ "proportional_size":1,\ "use_proportional_connected":False,\ "use_proportional_projected":False,\ "snap":False, "snap_elements":{'INCREMENT'},\ "use_snap_project":False, "snap_target":'CLOSEST',\ "use_snap_self":True, "use_snap_edit":True,\ "use_snap_nonedit":True, "use_snap_selectable":False,\ "snap_point":(0, 0, 0), "snap_align":False,\ "snap_normal":(0, 0, 0), "gpencil_strokes":False, \ "cursor_transform":False, "texture_space":False,\ "remove_on_cancel":False, "use_duplicated_keyframes":False,\ "view2d_edge_pan":False, "release_confirm":False,\ "use_accurate":False, "use_automerge_and_split":False}) bpy.ops.object.editmode_toggle() #End Edit mode #end def #円柱とボーンにウェイトを設定する。オブジェクト名を指定して1組だけ設定 def funcAutomaticWeight(firstObjName,SecondObjName): #next 1個ずつ親子設定にしていく、できれば親子関係がすでに設定されていれば何もしないようにしたいが、よくわからない gFindOne="" gFindTwo="" tmpObj="" #object mode にしないとオートウェイとが正しく動作しない print("now mode is ",bpy.context.mode) #editモードだったら、EDIT_ARMATURE のように出力される if bpy.context.mode == 'OBJECT': print("now OBJECT MODE") else: bpy.ops.object.editmode_toggle()#Objectモードに変更 #2回ループは非効率的だけど、今はこれでいく 1組のオブジェクトとボーンをウェイト設定 #Cylinder for obj in bpy.data.objects: if firstObjName in obj.name: print("find obj1") tmpObj=obj obj.select_set(True) gFindOne="ari" #Armature for obj in bpy.data.objects: if SecondObjName in obj.name: print("find obj2") obj.select_set(True) gFindTwo="ari" #できれば、すでに親子関係・ウェイトモードが設定されているかを判別したいけど、わからない if gFindOne=="ari" and gFindTwo=="ari": print("add parent do") #bone weight Automatic Weight Ctr+P オブジェクトモードで実行する必要がある bpy.ops.object.parent_set(type='ARMATURE_AUTO') else: print("いずれか一方または両方選択できませんでした") tmpObj.select_set(False)#Cylinder解除 bpy.ops.object.editmode_toggle()#Editモードに変更 #end def def funcSetWiggle2(arm,settingList):#armはアーマチュア名 #アドオン wiggle2がインストールされていることが前提 #settingListOne は、設定のリスト、パラメータ配列、ボーンの数によって設定は個別に異なる #head:root, tail:尻尾揺れる bpy.ops.object.mode_set(mode='POSE') print("Making Wiggle2") if len(settingList) < 1: print("配列のパラメータが足りません") #2024/10/15 14:36 #ArmatureからPoseのBone親を探して、子どもに設定する. gFindOne="" gFindTwo="" gWiggleBoneCount=0 #print("settingList[gWiggleBoneCount] :"+str(settingList[gWiggleBoneCount])) boneStrTmp="Bone.000" listCount=len(settingList) for obj in bpy.data.objects: if arm in obj.name: #print("find arm") obj.select_set(True) #gFindOne="ari" #Editモードに変更 pose モードにするためだけど効き目がないので無駄なコードかも for subbone in obj.pose.bones: print("bone::"+subbone.name) if "Bone" == subbone.name and gWiggleBoneCount == 0:# Bone in nameにすると含まれるになる print ("bone name:" + str(subbone.name)) #次はここから、 #☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆ #2024/10/15 うまくいってない #理由がわかった。ポーズモードでボーンに設定を与えるから、 #本来は、ポーズモードで、Amature>Pose>Bone、、、Bone007に対して設定を与える。 #正しくできなかったのは、Objedtモード状態の Amature>Armature.022>Bone、、、Bone007に設定してる気がする。 #最後に誤りの一番の理由は、Selectしたままにしたので、その値が最後の値で上書きされてしまった。selectを解除が必要だった #2024/10/15 #subbone.select_set(True) #ボーンではエラーになるamt.pose.bones['Bone'].bone.select = True subbone.bone.select=True bpy.context.scene.wiggle_enable = True bpy.context.active_pose_bone.wiggle_head = True #'NoneType' object has no attribute 'wiggle_head' ポーズモードになっていないとエラー bpy.context.active_pose_bone.wiggle_stiff_head = settingList[gWiggleBoneCount] bpy.context.active_pose_bone.wiggle_tail = False subbone.bone.select=False #解除してなかったので、上書きされた print("パラメータ:"+str(settingList[gWiggleBoneCount]) ) gWiggleBoneCount=1 #他にも設定があるので別の機会に試す else: #以下のIF部分は再度構成したほうがよい。たぶん入れ子の構想がまずい気がする。もっとスマートな書き方がある。2024/10/15 if gWiggleBoneCount >=1: #少なくとも親ボーン[0]が見つからないと処理しない。子ボーンから001になる boneStrTmpCount=len(boneStrTmp) boneStr="" if gWiggleBoneCount < 10:#1桁の場合 #print("ボーン1桁") #boneStrTmpの最後の1文字を数字に置き換える boneStr=boneStrTmp[0:boneStrTmpCount-1]+str(gWiggleBoneCount) #print("ボーン名"+boneStr) elif gWiggleBoneCount >= 10 and gWiggleBoneCount < 100: print("ボーン2桁") #boneStrTmpの最後の2文字を数字に置き換える boneStr=boneStrTmp[0:boneStrTmpCount-2]+str(gWiggleBoneCount)#ここは確認してない elif gWiggleBoneCount >= 100: print("100以上は対応できません。") break if boneStr == subbone.name: #子ボーン選択 forで対応。もしsettingListの要素数が足りない場合は、最後の値を使用する #2024/10/15 #subbone.select_set(True) #bpy.ops.object.mode_set(mode='POSE') subbone.bone.select=True bpy.context.active_pose_bone.wiggle_tail = True #settingListの数が、ボーンの数より小さいときは、最後の値をそのまま使う #2024/10/15 if (listCount-1) < gWiggleBoneCount: bpy.context.active_pose_bone.wiggle_stiff = settingList[-1]#最後の値を挿入 print("settingListの数が合っていないので最後の値を使用しました") else: bpy.context.active_pose_bone.wiggle_stiff = settingList[gWiggleBoneCount] print(boneStr+":パラメータ:"+str(settingList[gWiggleBoneCount]) ) gWiggleBoneCount+=1 subbone.bone.select=False #設定したら、選択を解除する #end def if __name__ == "__main__": #main GO #var gDepth=6# cylinder hight gCountBones=6# bone count over 2 number.limit count is [11 or 12]. << by gDepth:6 # 3Dカーソルの位置を元に戻す bpy.context.scene.cursor.location=(0.0,0.0,0.0) #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2) testLocation=(0.0,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,8) #location,high,bonesCountボーンの数 funcAutomaticWeight("Cylinder","Armature") settingListOne=[400,500,600,700,810,820,830,860]#ボーンの数分必要になる funcSetWiggle2("Armature",settingListOne) #現在作業中 bpy.ops.object.mode_set(mode='OBJECT') #2つめの円柱 testLocation=(1.5,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数 funcAutomaticWeight("Cylinder.001","Armature.001") settingListTwo=[410,800,800,800,800,880] funcSetWiggle2("Armature.001",settingListTwo) #3つめ testLocation=(3.0,0.0,gDepth/2) #x,y,z funcObjAndBones(testLocation,gDepth,4) #location,high,bonesCountボーンの数 funcAutomaticWeight("Cylinder.002","Armature.002") settingListThree=[420,800,800,810] funcSetWiggle2("Armature.002",settingListThree) #end main