forked from bookyakuno/Blender-Scramble-Addon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBONE_PT_constraints.py
215 lines (189 loc) · 6.12 KB
/
BONE_PT_constraints.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# 「プロパティ」エリア > 「ボーンコンストレイント」タブ
# "Propaties" Area > "Bone Constraints" Tab
import bpy
################
# オペレーター #
################
class QuickChildConstraint(bpy.types.Operator):
bl_idname = "constraint.quick_child_constraint"
bl_label = "Quick child"
bl_description = "Add child constraint to the active bone setting the selected bone as parent"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if 'selected_pose_bones' in dir(context):
if context.selected_pose_bones:
if 2 == len(context.selected_pose_bones):
return True
return False
def execute(self, context):
active_ob = context.active_object
active_bone = context.active_pose_bone
for target_bone in context.selected_pose_bones:
if active_bone.name != target_bone.name:
break
const = active_bone.constraints.new('CHILD_OF')
const.target = active_ob
const.subtarget = target_bone.name
override = context.copy()
override = {'constraint':const}
bpy.ops.constraint.childof_clear_inverse(override, constraint=const.name, owner='BONE')
bpy.ops.constraint.childof_set_inverse(override, constraint=const.name, owner='BONE')
return {'FINISHED'}
###############
# 個別処理 IK #
###############
class SetIkChainLength(bpy.types.Operator):
bl_idname = "pose.set_ik_chain_length"
bl_label = "Set IK Chain to Selected Bone"
bl_description = "Set chain length of the active bone's IK so that the chain's end is the selected bone"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if context.selected_pose_bones:
if len(context.selected_pose_bones) == 2:
for const in context.active_pose_bone.constraints:
if const.type == 'IK':
return True
return False
def execute(self, context):
activeBone = context.active_pose_bone
targetBone = None
for bone in context.selected_pose_bones:
if (activeBone.name != bone.name):
targetBone = bone
tempBone = activeBone
i = 0
while True:
if (tempBone.parent):
if (tempBone.name == targetBone.name):
i += 1
break
tempBone = tempBone.parent
i += 1
else:
i = 0
break
if (i == 0):
self.report(type={'ERROR'}, message="Failed to get chain counts")
return {'CANCELLED'}
ik = None
for const in activeBone.constraints:
if (const.type == "IK"):
ik = const
ik.chain_count = i
return {'FINISHED'}
class SetIkPoleTarget(bpy.types.Operator):
bl_idname = "pose.set_ik_pole_target"
bl_label = "Set Selected Bone as IK's Pole Target"
bl_description = "Set the selected bone as Pole Target of the active bone's IK"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if context.selected_pose_bones:
if len(context.selected_pose_bones) == 2:
for const in context.active_pose_bone.constraints:
if const.type == 'IK':
return True
return False
def execute(self, context):
activeObj = context.active_object
activeBone = context.active_pose_bone
for bone in context.selected_pose_bones:
if (activeBone.name != bone.name):
for const in activeBone.constraints:
if (const.type == "IK"):
ik = const
ik.pole_target = activeObj
ik.pole_subtarget = bone.name
return {'FINISHED'}
class SetIkPoleAngle(bpy.types.Operator):
bl_idname = "pose.set_ik_pole_angle"
bl_label = "Set IK's Pole Angle Based on Selected"
bl_description = "Set pole angle of the selected bone so that its location is equal to the rest position's one"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = context.active_object
if ob:
if ob.type == 'ARMATURE':
if context.selected_pose_bones:
if 1 <= len(context.selected_pose_bones):
for const in context.active_pose_bone.constraints:
if const.type == 'IK':
if const.pole_target:
return True
return False
def execute(self, context):
ob = context.active_object
arm = ob.data
for pose_bone in context.selected_pose_bones:
for const in pose_bone.constraints:
if const.type == 'IK':
if const.pole_target:
ik = const
break
else:
continue
bone = arm.bones[pose_bone.name]
ik.pole_angle = -3.1415926535897932384626433832795028841971
min_score = (ik.pole_angle, 9999999)
pre_angle = ik.pole_angle
for i in range(9999):
ik.pole_angle += 0.001
context.view_layer.update()
co = ( pose_bone.matrix.to_translation() - bone.head_local ).length
rot = pose_bone.matrix.to_quaternion().rotation_difference(bone.matrix_local.to_quaternion()).angle
score = co * rot
if score <= min_score[1]:
min_score = (ik.pole_angle, score)
if pre_angle == ik.pole_angle:
break
pre_angle = ik.pole_angle
ik.pole_angle = min_score[0]
context.view_layer.update()
return {'FINISHED'}
################
# サブメニュー #
################
class IKMenu(bpy.types.Menu):
bl_idname = "BONE_MT_constraints_ik"
bl_label = "Manipulate IK"
def draw(self, context):
self.layout.operator(SetIkChainLength.bl_idname, icon='PLUGIN')
self.layout.operator(SetIkPoleTarget.bl_idname, icon='PLUGIN')
self.layout.operator(SetIkPoleAngle.bl_idname, icon='PLUGIN')
################
# クラスの登録 #
################
classes = [
QuickChildConstraint,
SetIkChainLength,
SetIkPoleTarget,
SetIkPoleAngle,
IKMenu
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
################
# メニュー追加 #
################
# メニューのオン/オフの判定
def IsMenuEnable(self_id):
for id in bpy.context.preferences.addons[__name__.partition('.')[0]].preferences.disabled_menu.split(','):
if (id == self_id):
return False
else:
return True
# メニューを登録する関数
def menu(self, context):
if (IsMenuEnable(__name__.split('.')[-1])):
row = self.layout.row()
row.operator(QuickChildConstraint.bl_idname, icon='CONSTRAINT_BONE')
row.menu(IKMenu.bl_idname, icon='PLUGIN')
if (context.preferences.addons[__name__.partition('.')[0]].preferences.use_disabled_menu):
self.layout.operator('wm.toggle_menu_enable', icon='CANCEL').id = __name__.split('.')[-1]