Merge branch 'master' into blender2.8
[blender.git] / release / scripts / startup / bl_ui / properties_data_bone.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22 from bpy.types import Panel
23 from rna_prop_ui import PropertyPanel
24
25
26 class BoneButtonsPanel:
27     bl_space_type = 'PROPERTIES'
28     bl_region_type = 'WINDOW'
29     bl_context = "bone"
30
31     @classmethod
32     def poll(cls, context):
33         return (context.bone or context.edit_bone)
34
35
36 class BONE_PT_context_bone(BoneButtonsPanel, Panel):
37     bl_label = ""
38     bl_options = {'HIDE_HEADER'}
39
40     def draw(self, context):
41         layout = self.layout
42
43         bone = context.bone
44         if not bone:
45             bone = context.edit_bone
46
47         row = layout.row()
48         row.label(text="", icon='BONE_DATA')
49         row.prop(bone, "name", text="")
50
51
52 class BONE_PT_transform(BoneButtonsPanel, Panel):
53     bl_label = "Transform"
54
55     @classmethod
56     def poll(cls, context):
57         if context.edit_bone:
58             return True
59
60         ob = context.object
61         return ob and ob.mode == 'POSE' and context.bone
62
63     def draw(self, context):
64         layout = self.layout
65         layout.use_property_split = True
66
67         ob = context.object
68         bone = context.bone
69
70         col = layout.column()
71
72         if bone and ob:
73             pchan = ob.pose.bones[bone.name]
74             col.active = not (bone.parent and bone.use_connect)
75
76             sub = col.row(align=True)
77             sub.prop(pchan, "location")
78             sub.prop(pchan, "lock_location", text="")
79
80             col = layout.column()
81             if pchan.rotation_mode == 'QUATERNION':
82                 sub = col.row(align=True)
83                 sub.prop(pchan, "rotation_quaternion", text="Rotation")
84                 subsub = sub.column(align=True)
85                 subsub.prop(pchan, "lock_rotation_w", text="")
86                 subsub.prop(pchan, "lock_rotation", text="")
87             elif pchan.rotation_mode == 'AXIS_ANGLE':
88                 # col.label(text="Rotation")
89                 #col.prop(pchan, "rotation_angle", text="Angle")
90                 #col.prop(pchan, "rotation_axis", text="Axis")
91                 sub = col.row(align=True)
92                 sub.prop(pchan, "rotation_axis_angle", text="Rotation")
93                 subsub = sub.column(align=True)
94                 subsub.prop(pchan, "lock_rotation_w", text="")
95                 subsub.prop(pchan, "lock_rotation", text="")
96             else:
97                 sub = col.row(align=True)
98                 sub.prop(pchan, "rotation_euler", text="Rotation")
99                 sub.prop(pchan, "lock_rotation", text="")
100
101             col = layout.column()
102             sub = col.row(align=True)
103             sub.prop(pchan, "scale")
104             sub.prop(pchan, "lock_scale", text="")
105
106             col = layout.column()
107             col.prop(pchan, "rotation_mode")
108
109         elif context.edit_bone:
110             bone = context.edit_bone
111             col = layout.column()
112             col.prop(bone, "head")
113             col.prop(bone, "tail")
114
115             col = layout.column()
116             col.prop(bone, "roll")
117             col.prop(bone, "lock")
118
119             col = layout.column()
120             col.prop(bone, "tail_radius")
121             col.prop(bone, "envelope_distance")
122
123
124 class BONE_PT_curved(BoneButtonsPanel, Panel):
125     bl_label = "Bendy Bones"
126     #bl_options = {'DEFAULT_CLOSED'}
127
128     def draw(self, context):
129         ob = context.object
130         bone = context.bone
131         # arm = context.armature
132         pchan = None
133         edit = False
134
135         if ob and bone:
136             pchan = ob.pose.bones[bone.name]
137             bbone = pchan
138         elif bone is None:
139             bone = context.edit_bone
140             bbone = bone
141             edit = True
142         else:
143             bbone = bone
144
145         layout = self.layout
146         layout.use_property_split = True
147
148         layout.prop(bone, "bbone_segments", text="Segments")
149
150         topcol = layout.column()
151         topcol.active = bone.bbone_segments > 1
152
153         col = topcol.column(align=True)
154         col.prop(bbone, "bbone_curveinx", text="Curve In X")
155         col.prop(bbone, "bbone_curveiny", text="In Y")
156
157         col = topcol.column(align=True)
158         col.prop(bbone, "bbone_curveoutx", text="Curve Out X")
159         col.prop(bbone, "bbone_curveouty", text="Out Y")
160
161         col = topcol.column(align=True)
162         col.prop(bbone, "bbone_rollin", text="Roll In")
163         col.prop(bbone, "bbone_rollout", text="Out")
164         col.prop(bone, "use_endroll_as_inroll")
165
166         col = topcol.column(align=True)
167         col.prop(bbone, "bbone_scalein", text="Scale In")
168         col.prop(bbone, "bbone_scaleout", text="Out")
169
170         col = topcol.column(align=True)
171         col.prop(bbone, "bbone_easein", text="Ease In")
172         col.prop(bbone, "bbone_easeout", text="Out")
173
174         col = topcol.column(align=True)
175         col.prop(bone, "bbone_handle_type_start", text="Start Handle")
176
177         col = col.column(align=True)
178         col.active = (bone.bbone_handle_type_start != "AUTO")
179         if edit:
180             col.prop_search(bone, "bbone_custom_handle_start", ob.data, "edit_bones", text="Custom")
181         else:
182             # read-only
183             col.prop(bbone, "bbone_custom_handle_start", text="Custom")
184
185         col = topcol.column(align=True)
186         col.prop(bone, "bbone_handle_type_end", text="End Handle")
187
188         col = col.column(align=True)
189         col.active = (bone.bbone_handle_type_end != "AUTO")
190         if edit:
191             col.prop_search(bone, "bbone_custom_handle_end", ob.data, "edit_bones", text="Custom")
192         else:
193             # read-only
194             col.prop(bbone, "bbone_custom_handle_end", text="Custom")
195
196
197 class BONE_PT_relations(BoneButtonsPanel, Panel):
198     bl_label = "Relations"
199
200     def draw(self, context):
201         layout = self.layout
202         layout.use_property_split = True
203
204         ob = context.object
205         bone = context.bone
206         arm = context.armature
207         pchan = None
208
209         if ob and bone:
210             pchan = ob.pose.bones[bone.name]
211         elif bone is None:
212             bone = context.edit_bone
213
214         col = layout.column()
215         col.use_property_split = False
216         col.prop(bone, "layers", text="")
217         col.use_property_split = True
218         col = layout.column()
219
220         col.separator()
221
222         if context.bone:
223             col.prop(bone, "parent")
224         else:
225             col.prop_search(bone, "parent", arm, "edit_bones")
226
227         if ob and pchan:
228             col.prop(bone, "use_relative_parent")
229             col.prop_search(pchan, "bone_group", ob.pose, "bone_groups", text="Bone Group")
230
231         sub = col.column()
232         sub.active = (bone.parent is not None)
233         sub.prop(bone, "use_connect")
234         sub.prop(bone, "use_inherit_rotation")
235         sub.prop(bone, "use_inherit_scale")
236         sub = col.column()
237         sub.active = (not bone.parent or not bone.use_connect)
238         sub.prop(bone, "use_local_location")
239
240
241 class BONE_PT_display(BoneButtonsPanel, Panel):
242     bl_label = "Display"
243
244     @classmethod
245     def poll(cls, context):
246         return context.bone
247
248     def draw(self, context):
249         # note. this works ok in edit-mode but isn't
250         # all that useful so disabling for now.
251         layout = self.layout
252         layout.use_property_split = True
253
254         ob = context.object
255         bone = context.bone
256         pchan = None
257
258         if ob and bone:
259             pchan = ob.pose.bones[bone.name]
260         elif bone is None:
261             bone = context.edit_bone
262
263         if bone:
264
265             col = layout.column()
266             col.prop(bone, "hide", text="Hide")
267             sub = col.column()
268             sub.active = bool(pchan and pchan.custom_shape)
269             sub.prop(bone, "show_wire", text="Wireframe")
270
271             if pchan:
272                 col = layout.column()
273                 col.prop(pchan, "custom_shape")
274                 if pchan.custom_shape:
275                     col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size")
276                     col.prop(pchan, "custom_shape_scale", text="Scale")
277                     col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones")
278
279
280 class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
281     bl_label = "Inverse Kinematics"
282     bl_options = {'DEFAULT_CLOSED'}
283
284     @classmethod
285     def poll(cls, context):
286         ob = context.object
287         return ob and ob.mode == 'POSE' and context.bone
288
289     def draw(self, context):
290         layout = self.layout
291         layout.use_property_split = True
292
293         ob = context.object
294         bone = context.bone
295         pchan = ob.pose.bones[bone.name]
296
297         active = pchan.is_in_ik_chain
298
299         col = layout.column()
300         col.prop(pchan, "ik_stretch", slider=True)
301         col.active = active
302
303         layout.separator()
304
305         col = layout.column(align=True)
306
307         col.prop(pchan, "lock_ik_x", text="Lock IK X")
308         col.prop(pchan, "lock_ik_y", text="Y")
309         col.prop(pchan, "lock_ik_z", text="Z")
310
311         col = layout.column(align=True)
312
313         sub = col.column(align=True)
314         sub.active = pchan.lock_ik_x is False and active
315         sub.prop(pchan, "ik_stiffness_x", text="Stiffness X", slider=True)
316         sub = col.column(align=True)
317         sub.active = pchan.lock_ik_y is False and active
318         sub.prop(pchan, "ik_stiffness_y", text="Y", slider=True)
319         sub = col.column(align=True)
320         sub.active = pchan.lock_ik_z is False and active
321         sub.prop(pchan, "ik_stiffness_z", text="Z", slider=True)
322
323         col = layout.column(align=True)
324
325         sub = col.column()
326         sub.active = pchan.lock_ik_x is False and active
327         sub.prop(pchan, "use_ik_limit_x", text="Limit X")
328
329         sub = col.column(align=True)
330         sub.active = pchan.lock_ik_x is False and pchan.use_ik_limit_x and active
331         sub.prop(pchan, "ik_min_x", text="Min")
332         sub.prop(pchan, "ik_max_x", text="Max")
333
334         col.separator()
335
336         sub = col.column()
337         sub.active = pchan.lock_ik_y is False and active
338         sub.prop(pchan, "use_ik_limit_y", text="Limit Y")
339
340         sub = col.column(align=True)
341         sub.active = pchan.lock_ik_y is False and pchan.use_ik_limit_y and active
342         sub.prop(pchan, "ik_min_y", text="Min")
343         sub.prop(pchan, "ik_max_y", text="Max")
344
345         col.separator()
346
347         sub = col.column()
348         sub.active = pchan.lock_ik_z is False and active
349         sub.prop(pchan, "use_ik_limit_z", text="Limit Z")
350
351         sub = col.column(align=True)
352         sub.active = pchan.lock_ik_z is False and pchan.use_ik_limit_z and active
353         sub.prop(pchan, "ik_min_z", text="Min")
354         sub.prop(pchan, "ik_max_z", text="Max")
355
356         col.separator()
357
358         if ob.pose.ik_solver == 'ITASC':
359
360             col = layout.column()
361             col.prop(pchan, "use_ik_rotation_control", text="Control Rotation")
362             col.active = active
363
364             col = layout.column()
365
366             col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
367             col.active = active
368             # not supported yet
369             #row = layout.row()
370             #row.prop(pchan, "use_ik_linear_control", text="Joint Size")
371             #row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
372
373
374 class BONE_PT_deform(BoneButtonsPanel, Panel):
375     bl_label = "Deform"
376     bl_options = {'DEFAULT_CLOSED'}
377
378     def draw_header(self, context):
379         bone = context.bone
380
381         if not bone:
382             bone = context.edit_bone
383
384         self.layout.prop(bone, "use_deform", text="")
385
386     def draw(self, context):
387         layout = self.layout
388         layout.use_property_split = True
389
390         bone = context.bone
391
392         if not bone:
393             bone = context.edit_bone
394
395         layout.active = bone.use_deform
396
397         col = layout.column()
398         col.prop(bone, "envelope_distance", text="Envelope Distance")
399         col.prop(bone, "envelope_weight", text="Envelope Weight")
400         col.prop(bone, "use_envelope_multiply", text="Envelope Multiply")
401
402         col.separator()
403
404         col = layout.column(align=True)
405         col.prop(bone, "head_radius", text="Radius Head")
406         col.prop(bone, "tail_radius", text="Tail")
407
408
409 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
410     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
411     _property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
412
413     @property
414     def _context_path(self):
415         obj = bpy.context.object
416         if obj and obj.mode == 'POSE':
417             return "active_pose_bone"
418         else:
419             return "active_bone"
420
421
422 classes = (
423     BONE_PT_context_bone,
424     BONE_PT_transform,
425     BONE_PT_curved,
426     BONE_PT_relations,
427     BONE_PT_display,
428     BONE_PT_inverse_kinematics,
429     BONE_PT_deform,
430     BONE_PT_custom_props,
431 )
432
433 if __name__ == "__main__":  # only for live edit.
434     from bpy.utils import register_class
435     for cls in classes:
436         register_class(cls)