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