BlenderPythonスクリプトで、ボーンアニメーション移動の自動化

 アニメーション移動の自動化、ボーンを移動させるだけのスクリプト

仕様:円柱にボーンを複数設定し、ルートボーンを動かすアニメーションの自動化。これで複数の設定をしたボーンの実験を楽に確認できる。従来は、ボーンを作って設定を色々入れてアニメーション設定して確認して、、、、ここまでが面倒すぎてしかも後日(数ヶ月)その設定値を忘れてまた同じことの繰り返しだった。自動化できればすぐに再開できるし、新しい設定も楽しく試せる。

GIFアニメ、こんな感じでいろいろな設定を試して一番良さそうな揺れを探す

ファイル:make_obj_automticWeight_animation_class_014.py

GIF画像内の数値は手作業で入力した



ブログ内関連リンク、マテリアル設定スクリプト記事

前回の記事内リンク:Blender 自動化スクリプトで定形オブジェクト作成で楽をする、円柱とボーンを追加するWiggle2ポーズモード

https://kabujapan.blogspot.com/2024/10/blender_9.html

主なコマンド

bpy.context.scene.frame_set(localFrame)#でフレームを動かす
subbone.location.z = 0.1#Z軸方向に動かす
subbone.keyframe_insert(data_path="location")#キーフレームをlocationに対して挿入する。
"location"とか"scale"とか"rotation_euler"とかを入れる
これを実行するとタイムラインにキーが打たれる。
なお、ボーンの軸がグローバル軸と異なることがあるので、色々と数値を入れて確認する必要があった。例えばボーンをlocation.yで動かすとグローバルではZ軸(上方)に動いた。動かしたいのはグローバルのy軸だったけど。

bpy.context.scene.frame_end = 45#タイムラインのEndを250から45フレームに短く設定
bpy.context.scene.frame_set(0) # フレームを戻しておく
これができると実験が非常に楽になる。いろいろな設定をして、自動化アニメーションさせて確認が楽になる。
これを手作業だったら修行のように思える。

以下スクリプト、一部エラー処理とか入っていない。一部動作確認してない部分もある。前回の記事はWiggle2を設定したところまで。今回はキーフレームの自動化になるdef funcBoneSimpleAnimation()のところ。


  1. import os
  2. import bpy
  3. import math
  4. import copy
  5. import time
  6. #2024/10/9
  7. #print文はBlenderのメニューバーの Window > Toggle System Consoleを実行するとDOS窓が表示される
  8.  
  9. #make obj 円柱作成 Blender text ここでは日本語の入力はできないので、他で入力してコピペして
  10. #Next here 10/14
  11. #next code is 3 obj and 3 armatureBones. so make Function objAndBones(location,gDepth,gCountBones)
  12. def funcObjAndBones(mylocation,myDepth,myCountBones):
  13. #location=(0, 0, gDepth/2)
  14. loc=mylocation
  15. #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2)
  16. #中心点をDepth6m/2 半分Z軸を上げる
  17. # location=(0, 0, 3)#z is 3m , Depth is 6m, its half size.
  18. bpy.ops.mesh.primitive_cylinder_add(vertices=16,radius=0.5, depth=myDepth, enter_editmode=False,\
  19. align='WORLD', location=loc, scale=(1, 1, 1))
  20. #CTR+R bunkatsu 16 cuts
  21. #Start edit mode
  22. bpy.ops.object.editmode_toggle()
  23. bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut=
  24. {"number_cuts":16, "smoothness":0,\
  25. "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":15,\
  26. "mesh_select_mode_init":(True, False, False)},\
  27. TRANSFORM_OT_edge_slide={"value":0, "single_side":False,\
  28. "use_even":False, "flipped":False, \
  29. "use_clamp":True, "mirror":True, "snap":False, \
  30. "snap_elements":{'INCREMENT'}, "use_snap_project":False, \
  31. "snap_target":'CLOSEST', "use_snap_self":True, \
  32. "use_snap_edit":True, "use_snap_nonedit":True,\
  33. "use_snap_selectable":False, "snap_point":(0, 0, 0),\
  34. "correct_uv":True, "release_confirm":False, "use_accurate":False})
  35. bpy.ops.object.editmode_toggle()#from edit to OBJ mode
  36. # end edit mode
  37. #add bone 1
  38. #ボーンのlocationのZ軸は原点に戻す必要がある。
  39. loc=(mylocation[0],mylocation[1],mylocation[2]-myDepth/2)
  40. bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=loc, \
  41. scale=(1, 1, myDepth/myCountBones)) #6/4=1.5 Z軸スケールがなんかうまくいってない2024/10/15
  42. # Edit mode start
  43. bpy.ops.object.editmode_toggle()
  44. #add bone2 to (gCountBones-1)
  45. #ボーンの増加は、大きい親ボーンを分割処理してもいいのかも。試してみて。2024/10/15
  46. #E key EXTRUDE add child bone
  47. if myCountBones < 2:
  48. myCountBones=2
  49. #range(num) is 0 to num-1. exp: num=9 then 012345678 , its 9 counts. not exist [number9] .
  50. for i in range(myCountBones):# range() is from 0 to (gcountBones-1), total count is [gCountBOnes].
  51. if i >= myCountBones-1:
  52. break
  53. else:
  54. bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},\
  55. TRANSFORM_OT_translate={"value":(0, 0, myDepth/myCountBones),\
  56. "orient_type":'GLOBAL',\
  57. "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),\
  58. "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, True),\
  59. "mirror":False, "use_proportional_edit":False,\
  60. "proportional_edit_falloff":'SMOOTH',\
  61. "proportional_size":1,\
  62. "use_proportional_connected":False,\
  63. "use_proportional_projected":False,\
  64. "snap":False, "snap_elements":{'INCREMENT'},\
  65. "use_snap_project":False, "snap_target":'CLOSEST',\
  66. "use_snap_self":True, "use_snap_edit":True,\
  67. "use_snap_nonedit":True, "use_snap_selectable":False,\
  68. "snap_point":(0, 0, 0), "snap_align":False,\
  69. "snap_normal":(0, 0, 0), "gpencil_strokes":False, \
  70. "cursor_transform":False, "texture_space":False,\
  71. "remove_on_cancel":False, "use_duplicated_keyframes":False,\
  72. "view2d_edge_pan":False, "release_confirm":False,\
  73. "use_accurate":False, "use_automerge_and_split":False})
  74. bpy.ops.object.editmode_toggle()
  75. #End Edit mode
  76. #end def
  77.  
  78. #円柱とボーンにウェイトを設定する。オブジェクト名を指定して1組だけ設定
  79. def funcAutomaticWeight(firstObjName,SecondObjName):
  80. #next 1個ずつ親子設定にしていく、できれば親子関係がすでに設定されていれば何もしないようにしたいが、よくわからない
  81. gFindOne=""
  82. gFindTwo=""
  83. tmpObj=""
  84. #object mode にしないとオートウェイとが正しく動作しない
  85. print("now mode is ",bpy.context.mode) #editモードだったら、EDIT_ARMATURE のように出力される
  86. if bpy.context.mode == 'OBJECT':
  87. print("now OBJECT MODE")
  88. else:
  89. bpy.ops.object.editmode_toggle()#Objectモードに変更
  90. #2回ループは非効率的だけど、今はこれでいく 1組のオブジェクトとボーンをウェイト設定
  91. #Cylinder
  92. for obj in bpy.data.objects:
  93. if firstObjName in obj.name:
  94. print("find obj1")
  95. tmpObj=obj
  96. obj.select_set(True)
  97. gFindOne="ari"
  98. #Armature
  99. for obj in bpy.data.objects:
  100. if SecondObjName in obj.name:
  101. print("find obj2")
  102. obj.select_set(True)
  103. gFindTwo="ari"
  104.  
  105. #できれば、すでに親子関係・ウェイトモードが設定されているかを判別したいけど、わからない
  106. if gFindOne=="ari" and gFindTwo=="ari":
  107. print("add parent do")
  108. #bone weight Automatic Weight Ctr+P オブジェクトモードで実行する必要がある
  109. bpy.ops.object.parent_set(type='ARMATURE_AUTO')
  110. else:
  111. print("いずれか一方または両方選択できませんでした")
  112.  
  113. tmpObj.select_set(False)#Cylinder解除
  114. bpy.ops.object.editmode_toggle()#Editモードに変更
  115. #end def
  116.  
  117. def funcSetWiggle2(arm,settingList):#armはアーマチュア名
  118. #アドオン wiggle2がインストールされていることが前提
  119. #settingListOne は、設定のリスト、パラメータ配列、ボーンの数によって設定は個別に異なる
  120. #head:root, tail:尻尾揺れる
  121. bpy.ops.object.mode_set(mode='POSE')
  122.  
  123. print("Making Wiggle2")
  124. if len(settingList) < 1:
  125. print("配列のパラメータが足りません")
  126. #2024/10/15 14:36
  127. #ArmatureからPoseのBone親を探して、子どもに設定する.
  128. gFindOne=""
  129. gFindTwo=""
  130. gWiggleBoneCount=0
  131. #print("settingList[gWiggleBoneCount] :"+str(settingList[gWiggleBoneCount]))
  132. boneStrTmp="Bone.000"
  133. listCount=len(settingList)
  134. for obj in bpy.data.objects:
  135. if arm == obj.name:
  136. #print("find arm")
  137. obj.select_set(True)
  138. #gFindOne="ari"
  139. #Editモードに変更 pose モードにするためだけど効き目がないので無駄なコードかも
  140. for subbone in obj.pose.bones:
  141. print("bone::"+subbone.name)
  142. if "Bone" == subbone.name and gWiggleBoneCount == 0:# Bone in nameにすると含まれるになる
  143. print ("bone name:" + str(subbone.name))
  144. #次はここから、
  145. #☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
  146. #2024/10/15 うまくいってない
  147. #理由がわかった。ポーズモードでボーンに設定を与えるから、
  148. #本来は、ポーズモードで、Amature>Pose>Bone、、、Bone007に対して設定を与える。
  149. #正しくできなかったのは、Objedtモード状態の  Amature>Armature.022>Bone、、、Bone007に設定してる気がする。
  150. #最後に誤りの一番の理由は、Selectしたままにしたので、その値が最後の値で上書きされてしまった。selectを解除が必要だった
  151. #2024/10/15
  152. #subbone.select_set(True) #ボーンではエラーになるamt.pose.bones['Bone'].bone.select = True
  153. subbone.bone.select=True
  154. bpy.context.scene.wiggle_enable = True
  155. bpy.context.active_pose_bone.wiggle_head = True #'NoneType' object has no attribute 'wiggle_head' ポーズモードになっていないとエラー
  156. bpy.context.active_pose_bone.wiggle_stiff_head = settingList[gWiggleBoneCount]
  157. bpy.context.active_pose_bone.wiggle_tail = False
  158. subbone.bone.select=False #解除してなかったので、上書きされた
  159. print("パラメータ:"+str(settingList[gWiggleBoneCount]) )
  160. gWiggleBoneCount=1
  161. #他にも設定があるので別の機会に試す
  162. else:
  163. #以下のIF部分は再度構成したほうがよい。たぶん入れ子の構想がまずい気がする。もっとスマートな書き方がある。2024/10/15
  164. #ボーンが2桁のときの動作確認はしてない。エラーが出たらごめんね。
  165. if gWiggleBoneCount >=1: #少なくとも親ボーン[0]が見つからないと処理しない。子ボーンから001になる
  166. boneStrTmpCount=len(boneStrTmp)
  167. boneStr=""
  168. if gWiggleBoneCount < 10:#1桁の場合
  169. #print("ボーン1桁")
  170. #boneStrTmpの最後の1文字を数字に置き換える
  171. boneStr=boneStrTmp[0:boneStrTmpCount-1]+str(gWiggleBoneCount)
  172. #print("ボーン名"+boneStr)
  173. elif gWiggleBoneCount >= 10 and gWiggleBoneCount < 100:
  174. print("ボーン2桁")
  175. #boneStrTmpの最後の2文字を数字に置き換える
  176. boneStr=boneStrTmp[0:boneStrTmpCount-2]+str(gWiggleBoneCount)#ここは確認してない
  177. elif gWiggleBoneCount >= 100:
  178. print("100以上は対応できません。")
  179. break
  180.  
  181. if boneStr == subbone.name:
  182. #子ボーン選択 forで対応。もしsettingListの要素数が足りない場合は、最後の値を使用する
  183. #2024/10/15
  184. #subbone.select_set(True)
  185. #bpy.ops.object.mode_set(mode='POSE')
  186. subbone.bone.select=True
  187. bpy.context.active_pose_bone.wiggle_tail = True
  188. #settingListの数が、ボーンの数より小さいときは、最後の値をそのまま使う
  189. #2024/10/15
  190. if (listCount-1) < gWiggleBoneCount:
  191. bpy.context.active_pose_bone.wiggle_stiff = settingList[-1]#最後の値を挿入
  192. print("settingListの数が合っていないので最後の値を使用しました")
  193. else:
  194. bpy.context.active_pose_bone.wiggle_stiff = settingList[gWiggleBoneCount]
  195. print(boneStr+":パラメータ:"+str(settingList[gWiggleBoneCount]) )
  196. gWiggleBoneCount+=1
  197. subbone.bone.select=False #設定したら、選択を解除する
  198. #end def
  199. def funcBoneSimpleAnimation(arm,frame,y):#arm:Armature, frame:timeline frame, y: y distance
  200. #ArmatureのBoneを少し動かしてアニメーションの自動化補助程度の設定
  201. #ポーズモードで、ルートボーンを動かすアニメーションの設定までを自動化したい。
  202. #すでにArmatureが設定されていることが前提で、Armature名、ボーン名などを把握している必要がある
  203. #keyframe_insert ( data_path、 index = -1、 frame = bpy.context.scene.frame_current、 group = ''、 options = set()、 keytype = 'KEYFRAME' )
  204. #data_path="location"とか"scale"とか"rotation_euler"とかを入れる
  205. #index ( int ) – キーとなるプロパティの配列インデックス。デフォルトは -1
  206. #frame ( float ) – キーフレームが挿入されるフレーム。デフォルトは現在のフレームです。なのでbpy.context.scene.frame_set(N)すると不要みたい
  207. #以降は特に必要なときに調べる
  208. #
  209. print("funcBoneSimpleAnimation()")
  210. localFrame=0
  211. localFrame=frame
  212. bpy.ops.object.mode_set(mode='POSE')
  213. for obj in bpy.data.objects:
  214. if arm == obj.name:
  215. obj.select_set(True)
  216. for subbone in obj.pose.bones:
  217. print("bone::"+subbone.name)
  218. if "Bone" == subbone.name:# Bone in nameにすると含まれるになる
  219. print ("bone name:" + str(subbone.name))
  220. subbone.bone.select=True
  221. #animation move
  222. #Y軸に動かす、フレーム動かす、Y軸から原点に戻す、フレーム動かすの繰り返し
  223. #ボーンをlocation.yとするとグローバルではZ軸(上方)に動くので、Z軸に対して動かすとY軸に動く
  224. #bpy.context.area.ui_type = 'TIMELINE'
  225. #bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
  226. bpy.context.scene.frame_set(0)
  227. subbone.location.z = 0.0
  228. subbone.keyframe_insert(data_path="location")
  229.  
  230. bpy.context.scene.frame_set(localFrame)
  231. subbone.location.z = y
  232. #bpy.ops.transform.translate(value=(0, 1, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  233. subbone.keyframe_insert(data_path="location")
  234. localFrame+=frame
  235. bpy.context.scene.frame_set(localFrame)
  236. subbone.location.z = 0 #-y これだと激しいけど、面白いかも
  237. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  238. subbone.keyframe_insert(data_path="location")
  239. localFrame+=frame
  240. bpy.context.scene.frame_set(localFrame)
  241. subbone.location.z = y
  242. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  243. subbone.keyframe_insert(data_path="location")
  244. localFrame+=frame
  245.  
  246. bpy.context.scene.frame_set(localFrame)
  247. subbone.location.z = 0
  248. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  249. subbone.keyframe_insert(data_path="location")
  250.  
  251. #bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
  252. bpy.context.scene.frame_end = localFrame+5 #タイムラインのEndを250からキーフレーム+アルファに短く設定
  253. bpy.context.scene.frame_set(0) # フレームを戻しておく
  254. subbone.bone.select=False #選択解除
  255. return#1個しかないので抜ける
  256.  
  257. #end def
  258. if __name__ == "__main__":
  259. #main GO
  260. #var
  261. gDepth=6# cylinder hight
  262. gCountBones=6# bone count over 2 number.limit count is [11 or 12]. << by gDepth:6
  263. # 3Dカーソルの位置を元に戻す
  264. bpy.context.scene.cursor.location=(0.0,0.0,0.0)
  265. #obj.animation_data_clear()#アニメーションデータクリア
  266. tmp="Armature"#
  267. #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2)
  268. testLocation=(0.0,0.0,gDepth/2) #x,y,z
  269. funcObjAndBones(testLocation,gDepth,8) #location,high,bonesCountボーンの数
  270. funcAutomaticWeight("Cylinder",tmp)
  271. settingListOne=[400,500,600,700,800,850,900,1000]#ボーンの数分必要になる
  272. funcSetWiggle2(tmp,settingListOne) #現在作業中
  273. #bpy.ops.object.mode_set(mode='OBJECT')
  274. funcBoneSimpleAnimation(tmp,14,2)#アニメーション設定、移動させるだけ#arm:Armature名, frame:timeline frame, y: y distance
  275. bpy.ops.object.mode_set(mode='OBJECT')
  276.  
  277. tmp="Armature.001"
  278. #2つめの円柱
  279. testLocation=(1.5,0.0,gDepth/2) #x,y,z
  280. funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数
  281. funcAutomaticWeight("Cylinder.001",tmp)
  282. settingListTwo=[400,400,400,800,800,880]
  283. funcSetWiggle2(tmp,settingListTwo)
  284. funcBoneSimpleAnimation(tmp,14,2)
  285.  
  286. tmp="Armature.002"
  287. #3つめ
  288. testLocation=(3.0,0.0,gDepth/2) #x,y,z
  289. funcObjAndBones(testLocation,gDepth,4) #location,high,bonesCountボーンの数
  290. funcAutomaticWeight("Cylinder.002",tmp)
  291. settingListThree=[400,400,400,800]
  292. funcSetWiggle2(tmp,settingListThree)
  293. funcBoneSimpleAnimation(tmp,14,2)
  294.  
  295. #4つ
  296. tmp="Armature.003"
  297. testLocation=(4.5,0.0,gDepth/2) #x,y,z
  298. funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数
  299. funcAutomaticWeight("Cylinder.003",tmp)
  300. settingListThree=[400,800,800,800,900,1000]
  301. funcSetWiggle2(tmp,settingListThree)
  302. funcBoneSimpleAnimation(tmp,14,2)
  303. #5つ
  304. tmp="Armature.004"
  305. testLocation=(6.0,0.0,gDepth/2) #x,y,z
  306. funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数
  307. funcAutomaticWeight("Cylinder.004",tmp)
  308. settingListThree=[200,200,200,800,800,900]
  309. funcSetWiggle2(tmp,settingListThree)
  310. funcBoneSimpleAnimation(tmp,14,2)
  311.  
  312. #200
  313. tmp="Armature.005"
  314. testLocation=(7.5,0.0,gDepth/2) #x,y,z
  315. funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数
  316. funcAutomaticWeight("Cylinder.005",tmp)
  317. settingListThree=[200,200,200,200,200,200]
  318. funcSetWiggle2(tmp,settingListThree)
  319. funcBoneSimpleAnimation(tmp,14,2)
  320.  
  321.  
  322. #800
  323. tmp="Armature.006"
  324. testLocation=(9.0,0.0,gDepth/2) #x,y,z
  325. funcObjAndBones(testLocation,gDepth,6) #location,high,bonesCountボーンの数
  326. funcAutomaticWeight("Cylinder.006",tmp)
  327. settingListThree=[800,800,800,800,800,800]
  328. funcSetWiggle2(tmp,settingListThree)
  329. funcBoneSimpleAnimation(tmp,14,2)
  330.  
  331.  
  332.  
  333. #end main
更にClass化したコード、内容は同じ C:\Users\mased\Documents\Blender\blender\model\テスト、研究モデル\しっぽ揺れアニメ\Wiggle実験尻尾揺れ、2024年10月9日 make_obj_automticWeight_animation_class_014.py
  1.  
  2. import os
  3. import bpy
  4. import math
  5. import copy
  6. import time
  7. import inspect#関数名を出力するためだけのもの
  8. #2024/10/9
  9. #print文はBlenderのメニューバーの Window > Toggle System Consoleを実行するとDOS窓が表示される
  10.  
  11. #make obj 円柱作成 Blender text ここでは日本語の入力はできないので、他で入力してコピペして
  12. #Next here 10/14
  13. #next code is 3 obj and 3 armatureBones. so make Function objAndBones(location,gDepth,gCountBones)
  14. def funcObjAndBones(mylocation,myDepth,myCountBones):
  15. #location=(0, 0, gDepth/2)
  16. loc=mylocation
  17. #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2)
  18. #中心点をDepth6m/2 半分Z軸を上げる
  19. # location=(0, 0, 3)#z is 3m , Depth is 6m, its half size.
  20. bpy.ops.mesh.primitive_cylinder_add(vertices=16,radius=0.5, depth=myDepth, enter_editmode=False,\
  21. align='WORLD', location=loc, scale=(1, 1, 1))
  22. #CTR+R bunkatsu 16 cuts
  23. #Start edit mode
  24. bpy.ops.object.editmode_toggle()
  25. bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut=
  26. {"number_cuts":16, "smoothness":0,\
  27. "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":15,\
  28. "mesh_select_mode_init":(True, False, False)},\
  29. TRANSFORM_OT_edge_slide={"value":0, "single_side":False,\
  30. "use_even":False, "flipped":False, \
  31. "use_clamp":True, "mirror":True, "snap":False, \
  32. "snap_elements":{'INCREMENT'}, "use_snap_project":False, \
  33. "snap_target":'CLOSEST', "use_snap_self":True, \
  34. "use_snap_edit":True, "use_snap_nonedit":True,\
  35. "use_snap_selectable":False, "snap_point":(0, 0, 0),\
  36. "correct_uv":True, "release_confirm":False, "use_accurate":False})
  37. bpy.ops.object.editmode_toggle()#from edit to OBJ mode
  38. # end edit mode
  39. #add bone 1
  40. #ボーンのlocationのZ軸は原点に戻す必要がある。
  41. loc=(mylocation[0],mylocation[1],mylocation[2]-myDepth/2)
  42. bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=loc, \
  43. scale=(1, 1, myDepth/myCountBones)) #6/4=1.5 Z軸スケールがなんかうまくいってない2024/10/15
  44. # Edit mode start
  45. bpy.ops.object.editmode_toggle()
  46. #add bone2 to (gCountBones-1)
  47. #ボーンの増加は、大きい親ボーンを分割処理してもいいのかも。試してみて。2024/10/15
  48. #E key EXTRUDE add child bone
  49. if myCountBones < 2:
  50. myCountBones=2
  51. #range(num) is 0 to num-1. exp: num=9 then 012345678 , its 9 counts. not exist [number9] .
  52. for i in range(myCountBones):# range() is from 0 to (gcountBones-1), total count is [gCountBOnes].
  53. if i >= myCountBones-1:
  54. break
  55. else:
  56. bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},\
  57. TRANSFORM_OT_translate={"value":(0, 0, myDepth/myCountBones),\
  58. "orient_type":'GLOBAL',\
  59. "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),\
  60. "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, True),\
  61. "mirror":False, "use_proportional_edit":False,\
  62. "proportional_edit_falloff":'SMOOTH',\
  63. "proportional_size":1,\
  64. "use_proportional_connected":False,\
  65. "use_proportional_projected":False,\
  66. "snap":False, "snap_elements":{'INCREMENT'},\
  67. "use_snap_project":False, "snap_target":'CLOSEST',\
  68. "use_snap_self":True, "use_snap_edit":True,\
  69. "use_snap_nonedit":True, "use_snap_selectable":False,\
  70. "snap_point":(0, 0, 0), "snap_align":False,\
  71. "snap_normal":(0, 0, 0), "gpencil_strokes":False, \
  72. "cursor_transform":False, "texture_space":False,\
  73. "remove_on_cancel":False, "use_duplicated_keyframes":False,\
  74. "view2d_edge_pan":False, "release_confirm":False,\
  75. "use_accurate":False, "use_automerge_and_split":False})
  76. bpy.ops.object.editmode_toggle()
  77. #End Edit mode
  78. #end def
  79.  
  80. #円柱とボーンにウェイトを設定する。オブジェクト名を指定して1組だけ設定
  81. def funcAutomaticWeight(firstObjName,SecondObjName):
  82. #next 1個ずつ親子設定にしていく、できれば親子関係がすでに設定されていれば何もしないようにしたいが、よくわからない
  83. gFindOne=""
  84. gFindTwo=""
  85. tmpObj=""
  86. #object mode にしないとオートウェイとが正しく動作しない
  87. print("now mode is ",bpy.context.mode) #editモードだったら、EDIT_ARMATURE のように出力される
  88. if bpy.context.mode == 'OBJECT':
  89. print("now OBJECT MODE")
  90. else:
  91. bpy.ops.object.editmode_toggle()#Objectモードに変更
  92. #2回ループは非効率的だけど、今はこれでいく 1組のオブジェクトとボーンをウェイト設定
  93. #Cylinder
  94. for obj in bpy.data.objects:
  95. if firstObjName in obj.name:
  96. print("find obj1")
  97. tmpObj=obj
  98. obj.select_set(True)
  99. gFindOne="ari"
  100. #Armature
  101. for obj in bpy.data.objects:
  102. if SecondObjName in obj.name:
  103. print("find obj2")
  104. obj.select_set(True)
  105. gFindTwo="ari"
  106.  
  107. #できれば、すでに親子関係・ウェイトモードが設定されているかを判別したいけど、わからない
  108. if gFindOne=="ari" and gFindTwo=="ari":
  109. print("add parent do")
  110. #bone weight Automatic Weight Ctr+P オブジェクトモードで実行する必要がある
  111. bpy.ops.object.parent_set(type='ARMATURE_AUTO')
  112. else:
  113. print("いずれか一方または両方選択できませんでした")
  114.  
  115. tmpObj.select_set(False)#Cylinder解除
  116. bpy.ops.object.editmode_toggle()#Editモードに変更
  117. #end def
  118.  
  119. def funcSetWiggle2(arm,settingList):#armはアーマチュア名
  120. #アドオン wiggle2がインストールされていることが前提
  121. #settingListOne は、設定のリスト、パラメータ配列、ボーンの数によって設定は個別に異なる
  122. #head:root, tail:尻尾揺れる
  123. bpy.ops.object.mode_set(mode='POSE')
  124.  
  125. print("Making Wiggle2")
  126. if len(settingList) < 1:
  127. print("配列のパラメータが足りません")
  128. #2024/10/15 14:36
  129. #ArmatureからPoseのBone親を探して、子どもに設定する.
  130. gFindOne=""
  131. gFindTwo=""
  132. gWiggleBoneCount=0
  133. #print("settingList[gWiggleBoneCount] :"+str(settingList[gWiggleBoneCount]))
  134. boneStrTmp="Bone.000"
  135. listCount=len(settingList)
  136. for obj in bpy.data.objects:
  137. if arm == obj.name:
  138. #print("find arm")
  139. obj.select_set(True)
  140. #gFindOne="ari"
  141. #Editモードに変更 pose モードにするためだけど効き目がないので無駄なコードかも
  142. for subbone in obj.pose.bones:
  143. print("bone::"+subbone.name)
  144. if "Bone" == subbone.name and gWiggleBoneCount == 0:# Bone in nameにすると含まれるになる
  145. print ("bone name:" + str(subbone.name))
  146. #次はここから、
  147. #☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
  148. #2024/10/15 うまくいってない
  149. #理由がわかった。ポーズモードでボーンに設定を与えるから、
  150. #本来は、ポーズモードで、Amature>Pose>Bone、、、Bone007に対して設定を与える。
  151. #正しくできなかったのは、Objedtモード状態の  Amature>Armature.022>Bone、、、Bone007に設定してる気がする。
  152. #最後に誤りの一番の理由は、Selectしたままにしたので、その値が最後の値で上書きされてしまった。selectを解除が必要だった
  153. #2024/10/15
  154. #subbone.select_set(True) #ボーンではエラーになるamt.pose.bones['Bone'].bone.select = True
  155. subbone.bone.select=True
  156. bpy.context.scene.wiggle_enable = True
  157. bpy.context.active_pose_bone.wiggle_head = True #'NoneType' object has no attribute 'wiggle_head' ポーズモードになっていないとエラー
  158. bpy.context.active_pose_bone.wiggle_stiff_head = settingList[gWiggleBoneCount]
  159. bpy.context.active_pose_bone.wiggle_tail = False
  160. subbone.bone.select=False #解除してなかったので、上書きされた
  161. print("パラメータ:"+str(settingList[gWiggleBoneCount]) )
  162. gWiggleBoneCount=1
  163. #他にも設定があるので別の機会に試す
  164. else:
  165. #以下のIF部分は再度構成したほうがよい。たぶん入れ子の構想がまずい気がする。もっとスマートな書き方がある。2024/10/15
  166. #ボーンが2桁のときの動作確認はしてない。エラーが出たらごめんね。
  167. if gWiggleBoneCount >=1: #少なくとも親ボーン[0]が見つからないと処理しない。子ボーンから001になる
  168. boneStrTmpCount=len(boneStrTmp)
  169. boneStr=""
  170. if gWiggleBoneCount < 10:#1桁の場合
  171. #print("ボーン1桁")
  172. #boneStrTmpの最後の1文字を数字に置き換える
  173. boneStr=boneStrTmp[0:boneStrTmpCount-1]+str(gWiggleBoneCount)
  174. #print("ボーン名"+boneStr)
  175. elif gWiggleBoneCount >= 10 and gWiggleBoneCount < 100:
  176. print("ボーン2桁")
  177. #boneStrTmpの最後の2文字を数字に置き換える
  178. boneStr=boneStrTmp[0:boneStrTmpCount-2]+str(gWiggleBoneCount)#ここは確認してない
  179. elif gWiggleBoneCount >= 100:
  180. print("100以上は対応できません。")
  181. break
  182.  
  183. if boneStr == subbone.name:
  184. #子ボーン選択 forで対応。もしsettingListの要素数が足りない場合は、最後の値を使用する
  185. #2024/10/15
  186. #subbone.select_set(True)
  187. #bpy.ops.object.mode_set(mode='POSE')
  188. subbone.bone.select=True
  189. bpy.context.active_pose_bone.wiggle_tail = True
  190. #settingListの数が、ボーンの数より小さいときは、最後の値をそのまま使う
  191. #2024/10/15
  192. if (listCount-1) < gWiggleBoneCount:
  193. bpy.context.active_pose_bone.wiggle_stiff = settingList[-1]#最後の値を挿入
  194. print("settingListの数が合っていないので最後の値を使用しました")
  195. else:
  196. bpy.context.active_pose_bone.wiggle_stiff = settingList[gWiggleBoneCount]
  197. print(boneStr+":パラメータ:"+str(settingList[gWiggleBoneCount]) )
  198. gWiggleBoneCount+=1
  199. subbone.bone.select=False #設定したら、選択を解除する
  200. #end def
  201. def funcBoneSimpleAnimation(arm,frame,y):#arm:Armature, frame:timeline frame, y: y distance
  202. #ArmatureのBoneを少し動かしてアニメーションの自動化補助程度の設定
  203. #ポーズモードで、ルートボーンを動かすアニメーションの設定までを自動化したい。
  204. #すでにArmatureが設定されていることが前提で、Armature名、ボーン名などを把握している必要がある
  205. #keyframe_insert ( data_path、 index = -1、 frame = bpy.context.scene.frame_current、 group = ''、 options = set()、 keytype = 'KEYFRAME' )
  206. #data_path="location"とか"scale"とか"rotation_euler"とかを入れる
  207. #index ( int ) – キーとなるプロパティの配列インデックス。デフォルトは -1
  208. #frame ( float ) – キーフレームが挿入されるフレーム。デフォルトは現在のフレームです。なのでbpy.context.scene.frame_set(N)すると不要みたい
  209. #以降は特に必要なときに調べる
  210. #
  211. print("funcBoneSimpleAnimation()")
  212. localFrame=0
  213. localFrame=frame
  214. bpy.ops.object.mode_set(mode='POSE')
  215. for obj in bpy.data.objects:
  216. if arm == obj.name:
  217. obj.select_set(True)
  218. for subbone in obj.pose.bones:
  219. print("bone::"+subbone.name)
  220. if "Bone" == subbone.name:# Bone in nameにすると含まれるになる
  221. print ("bone name:" + str(subbone.name))
  222. subbone.bone.select=True
  223. #animation move
  224. #Y軸に動かす、フレーム動かす、Y軸から原点に戻す、フレーム動かすの繰り返し
  225. #ボーンをlocation.yとするとグローバルではZ軸(上方)に動くので、Z軸に対して動かすとY軸に動く
  226. #bpy.context.area.ui_type = 'TIMELINE'
  227. #bpy.context.scene.tool_settings.use_keyframe_insert_auto = True
  228. bpy.context.scene.frame_set(0)
  229. subbone.location.z = 0.0
  230. subbone.keyframe_insert(data_path="location")
  231.  
  232. bpy.context.scene.frame_set(localFrame)
  233. subbone.location.z = y
  234. #bpy.ops.transform.translate(value=(0, 1, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  235. subbone.keyframe_insert(data_path="location")
  236. localFrame+=frame
  237. bpy.context.scene.frame_set(localFrame)
  238. subbone.location.z = 0 #-y これだと激しいけど、面白いかも
  239. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  240. subbone.keyframe_insert(data_path="location")
  241. localFrame+=frame
  242. bpy.context.scene.frame_set(localFrame)
  243. subbone.location.z = y
  244. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  245. subbone.keyframe_insert(data_path="location")
  246. localFrame+=frame
  247.  
  248. bpy.context.scene.frame_set(localFrame)
  249. subbone.location.z = 0
  250. #bpy.ops.transform.translate(value=(-0, -1, -0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=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)
  251. subbone.keyframe_insert(data_path="location")
  252.  
  253. #bpy.context.scene.tool_settings.use_keyframe_insert_auto = False
  254. bpy.context.scene.frame_end = localFrame+5 #タイムラインのEndを250からキーフレーム+アルファに短く設定
  255. bpy.context.scene.frame_set(0) # フレームを戻しておく
  256. subbone.bone.select=False #選択解除
  257. return#1個しかないので抜ける
  258. #end def
  259. def funcMatome():
  260. #ClassCapcelParamaterを使用。クラス化したものをまとめる,クラスにしたことで、オブジェクトが増えてもコード量はそれほど増えない
  261. #以前は1オブジェクト増加で10行くらいコードが増えたけど、カプセル化で+1行になった(配列Append分)
  262. gDepth=6# cylinder hight
  263. #gCountBones=6#
  264. testLocation=(0.0, 0.0, gDepth/2) #x,y,z
  265.  
  266. gClassList=[]#class capcel
  267. gParamaterList=[]#Wiggle2のパラメータのリスト 200柔らか、>>> 800固い
  268. #gParamaterList.append({400,500,600,700,800,850})#中括弧これだとうまくいかない なんでだ?{}で囲ったのが原因だった
  269. #[]大カッコにした これで正解
  270. gParamaterList.append([800,800,800,800,800,800])#配列の要素数は2個以上99未満の個数で記載。要素数は同じ数である必要はない。がわかりやすく同じにしてるだけ
  271. gParamaterList.append([200,200,200,200,200,200])
  272. gParamaterList.append([200,300,400,500,600,750])
  273. gParamaterList.append([800,300,300,300,300,200])
  274. gParamaterList.append([800,700,600,400,300,200])
  275.  
  276. tmpParamaterListCount=len(gParamaterList)
  277. print("gParamaterList count:"+str(tmpParamaterListCount))
  278.  
  279. tmpArmStr="Armature.000"#Armature.001みたいにする
  280. tmpArmStrCount=len(tmpArmStr)
  281.  
  282. tmpCylinderStr="Cylinder.000"
  283. tmpCylinderStrCount=len(tmpCylinderStr)
  284. tmpCapcell=""
  285. tmpKyori=0
  286. for i in range(tmpParamaterListCount):
  287. tmpCapcell=""
  288. if i==0:#0のときは、番号なし
  289. subArmStr="Armature"
  290. subCylinderStr="Cylinder"
  291. else:
  292. if tmpParamaterListCount < 10:#桁の場合 001009となる
  293. subArmStr= tmpArmStr[0:tmpArmStrCount-1]+str(i)
  294. subCylinderStr= tmpCylinderStr[0:tmpCylinderStrCount-1]+str(i)
  295. elif tmpParamaterListCount >=10 and tmpParamaterListCount < 100:
  296. subArmStr= tmpArmStr[0:tmpArmStrCount-2]+str(i)
  297. subCylinderStr= tmpCylinderStr[0:tmpCylinderStrCount-2]+str(i)
  298. else:
  299. print("100を超えたので、許容オーバーです。配列の数を99個にしてください")
  300. return
  301. print("gParamaterList[i]="+str(gParamaterList[i]))
  302. tmpCapcell=ClassCapcelParamater(tmpKyori, subArmStr, subCylinderStr, gParamaterList[i])
  303. tmpCapcell.myNamePrint()#自分のクラス名を出力するだけのもの、何に使うかというと単に使用しているクラス名が知りたいだけ、コピペだと誤記が発生するから
  304. tmpKyori+=1.5#オブジェクトの間隔固定。#円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2)
  305. gClassList.append(tmpCapcell)#カプセルにしなくてもよいような気がしてるけどまとめたら便利だろう
  306.  
  307. #オブジェクトのボーンの数で作業
  308. for i in range(tmpParamaterListCount):
  309. testLocation=(float(gClassList[i].kyori), 0.0, gDepth/2)
  310. #次はここから2024/10/16
  311. print("len(gClassList[i].list):"+str(len(gClassList[i].list)))
  312. print("gClassList[i].armName:"+str(gClassList[i].armName))
  313.  
  314. funcObjAndBones(testLocation, gDepth, len(gClassList[i].list)) #location,high,bonesCountボーンの数,この段階で配列の要素は使用していない
  315. funcAutomaticWeight(gClassList[i].objName, gClassList[i].armName)
  316. funcSetWiggle2(gClassList[i].armName, gClassList[i].list)#ここで、配列の要素パラメータが必要になる
  317. funcBoneSimpleAnimation(gClassList[i].armName, 14, 2)#アニメーション設定、移動させるだけ#arm:Armature名, frame:timeline frame, y: y distance
  318. #end for
  319. #end def
  320.  
  321. #class 各種パラメータ、設定値をカプセル化にして、更に配列にカプセルを挿入させることでコードの視認性をたかめる
  322. #funcMatome()内で使用する
  323. class ClassCapcelParamater:
  324. def __init__(self,kyori,armName,objName,list) -> None:
  325. print("class const")
  326. self.kyori= kyori#オブジェクトのX座標
  327. self.armName= armName#Armature name:Armature
  328. self.objName= objName#Obj type name:Cylinder
  329. self.list= list#Wiggle2のStiffのパラメータ配列、ボーンの数だけ必要なので、ボーンの数はこの配列の要素数で自動で取得
  330. self.number= 0
  331.  
  332. def __del__(self):
  333. print("del デストラクタ、何もしないけど")
  334.  
  335. def myNamePrint(self):# class 自分自身 出力
  336. function_name = inspect.currentframe().f_code.co_name# この部分は関数中でPrintで使える
  337. class_name = self.__class__.__name__
  338. print('{}.{}'.format(function_name, class_name))
  339. #end Class
  340.  
  341. if __name__ == "__main__":
  342. print("*************************************************")
  343. print("*************************************************")
  344. print("****************** Start ***********************")
  345. #main GO
  346. #gDepth=6# cylinder hight
  347. #gCountBones=6# bone count over 2 number.limit count is [11 or 12]. << by gDepth:6
  348. # 3Dカーソルの位置を元に戻す
  349. bpy.context.scene.cursor.location=(0.0,0.0,0.0)
  350. #obj.animation_data_clear()#アニメーションデータクリア
  351. funcMatome()#Class capcelをまとめたもの
  352.  
  353. print("****************** end ***********************")
  354. '''
  355. #Stiff(固さ)は数値が低いほど柔らかい、数値が高いほど固くなる
  356. tmp="Armature"#
  357. #円柱Vertices16,半径0.5m、Depth6m 座標:0,0,3m(Depth6m/2)
  358. testLocation=(0.0,0.0,gDepth/2) #x,y,z
  359. funcObjAndBones(testLocation,gDepth,8) #location,high,bonesCountボーンの数
  360. funcAutomaticWeight("Cylinder",tmp)
  361. settingListOne=[400,500,600,700,800,850,900,1000]#ボーンの数分必要になる
  362. funcSetWiggle2(tmp,settingListOne) #
  363. funcBoneSimpleAnimation(tmp,14,2)#アニメーション設定、移動させるだけ#arm:Armature名, frame:timeline frame, y: y distance
  364. bpy.ops.object.mode_set(mode='OBJECT')
  365. '''
  366. #end main

このブログの人気の投稿

国税庁確定申告でエラーになったところ株式譲渡が赤字の場合には、配当を申告不要とすることはできません

メモ、BlenderPythonスクリプトで参考になるオブジェクトや頂点の選択ツリー選択スクリプトリンクメモ

楽天証券、信用取引口座から楽天FX口座へ振替手順