Merge remote-tracking branch 'origin/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_options = {'DEFAULT_CLOSED'}
199     bl_label = "Relations"
200
201     def draw(self, context):
202         layout = self.layout
203         layout.use_property_split = True
204
205         ob = context.object
206         bone = context.bone
207         arm = context.armature
208         pchan = None
209
210         if ob and bone:
211             pchan = ob.pose.bones[bone.name]
212         elif bone is None:
213             bone = context.edit_bone
214
215         col = layout.column()
216         col.use_property_split = False
217         col.prop(bone, "layers", text="")
218         col.use_property_split = True
219         col = layout.column()
220
221         col.separator()
222
223         if context.bone:
224             col.prop(bone, "parent")
225         else:
226             col.prop_search(bone, "parent", arm, "edit_bones")
227
228         if ob and pchan:
229             col.prop(bone, "use_relative_parent")
230             col.prop_search(pchan, "bone_group", ob.pose, "bone_groups", text="Bone Group")
231
232         sub = col.column()
233         sub.active = (bone.parent is not None)
234         sub.prop(bone, "use_connect")
235         sub.prop(bone, "use_inherit_rotation")
236         sub.prop(bone, "use_inherit_scale")
237         sub = col.column()
238         sub.active = (not bone.parent or not bone.use_connect)
239         sub.prop(bone, "use_local_location")
240
241
242 class BONE_PT_display(BoneButtonsPanel, Panel):
243     bl_label = "Display"
244     bl_options = {'DEFAULT_CLOSED'}
245
246     @classmethod
247     def poll(cls, context):
248         return context.bone
249
250     def draw(self, context):
251         # note. this works ok in edit-mode but isn't
252         # all that useful so disabling for now.
253         layout = self.layout
254         layout.use_property_split = True
255
256         ob = context.object
257         bone = context.bone
258         pchan = None
259
260         if ob and bone:
261             pchan = ob.pose.bones[bone.name]
262         elif bone is None:
263             bone = context.edit_bone
264
265         if bone:
266
267             col = layout.column()
268             col.prop(bone, "hide", text="Hide")
269             sub = col.column()
270             sub.active = bool(pchan and pchan.custom_shape)
271             sub.prop(bone, "show_wire", text="Wireframe")
272
273             if pchan:
274                 col = layout.column()
275                 col.prop(pchan, "custom_shape")
276                 if pchan.custom_shape:
277                     col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size")
278                     col.prop(pchan, "custom_shape_scale", text="Scale")
279                     col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones")
280
281
282 class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
283     bl_label = "Inverse Kinematics"
284     bl_options = {'DEFAULT_CLOSED'}
285
286     @classmethod
287     def poll(cls, context):
288         ob = context.object
289         return ob and ob.mode == 'POSE' and context.bone
290
291     def draw(self, context):
292         layout = self.layout
293         layout.use_property_split = True
294
295         ob = context.object
296         bone = context.bone
297         pchan = ob.pose.bones[bone.name]
298
299         active = pchan.is_in_ik_chain
300
301         col = layout.column()
302         col.prop(pchan, "ik_stretch", slider=True)
303         col.active = active
304
305         layout.separator()
306
307         col = layout.column(align=True)
308
309         col.prop(pchan, "lock_ik_x", text="Lock IK X")
310         col.prop(pchan, "lock_ik_y", text="Y")
311         col.prop(pchan, "lock_ik_z", text="Z")
312
313         col = layout.column(align=True)
314
315         sub = col.column(align=True)
316         sub.active = pchan.lock_ik_x is False and active
317         sub.prop(pchan, "ik_stiffness_x", text="Stiffness X", slider=True)
318         sub = col.column(align=True)
319         sub.active = pchan.lock_ik_y is False and active
320         sub.prop(pchan, "ik_stiffness_y", text="Y", slider=True)
321         sub = col.column(align=True)
322         sub.active = pchan.lock_ik_z is False and active
323         sub.prop(pchan, "ik_stiffness_z", text="Z", slider=True)
324
325         col = layout.column(align=True)
326
327         sub = col.column()
328         sub.active = pchan.lock_ik_x is False and active
329         sub.prop(pchan, "use_ik_limit_x", text="Limit X")
330
331         sub = col.column(align=True)
332         sub.active = pchan.lock_ik_x is False and pchan.use_ik_limit_x and active
333         sub.prop(pchan, "ik_min_x", text="Min")
334         sub.prop(pchan, "ik_max_x", text="Max")
335
336         col.separator()
337
338         sub = col.column()
339         sub.active = pchan.lock_ik_y is False and active
340         sub.prop(pchan, "use_ik_limit_y", text="Limit Y")
341
342         sub = col.column(align=True)
343         sub.active = pchan.lock_ik_y is False and pchan.use_ik_limit_y and active
344         sub.prop(pchan, "ik_min_y", text="Min")
345         sub.prop(pchan, "ik_max_y", text="Max")
346
347         col.separator()
348
349         sub = col.column()
350         sub.active = pchan.lock_ik_z is False and active
351         sub.prop(pchan, "use_ik_limit_z", text="Limit Z")
352
353         sub = col.column(align=True)
354         sub.active = pchan.lock_ik_z is False and pchan.use_ik_limit_z and active
355         sub.prop(pchan, "ik_min_z", text="Min")
356         sub.prop(pchan, "ik_max_z", text="Max")
357
358         col.separator()
359
360         if ob.pose.ik_solver == 'ITASC':
361
362             col = layout.column()
363             col.prop(pchan, "use_ik_rotation_control", text="Control Rotation")
364             col.active = active
365
366             col = layout.column()
367
368             col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
369             col.active = active
370             # not supported yet
371             #row = layout.row()
372             #row.prop(pchan, "use_ik_linear_control", text="Joint Size")
373             #row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
374
375
376 class BONE_PT_deform(BoneButtonsPanel, Panel):
377     bl_label = "Deform"
378     bl_options = {'DEFAULT_CLOSED'}
379
380     def draw_header(self, context):
381         bone = context.bone
382
383         if not bone:
384             bone = context.edit_bone
385
386         self.layout.prop(bone, "use_deform", text="")
387
388     def draw(self, context):
389         layout = self.layout
390         layout.use_property_split = True
391
392         bone = context.bone
393
394         if not bone:
395             bone = context.edit_bone
396
397         layout.active = bone.use_deform
398
399         col = layout.column()
400         col.prop(bone, "envelope_distance", text="Envelope Distance")
401         col.prop(bone, "envelope_weight", text="Envelope Weight")
402         col.prop(bone, "use_envelope_multiply", text="Envelope Multiply")
403
404         col.separator()
405
406         col = layout.column(align=True)
407         col.prop(bone, "head_radius", text="Radius Head")
408         col.prop(bone, "tail_radius", text="Tail")
409
410
411 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
412     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
413     _property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
414
415     @property
416     def _context_path(self):
417         obj = bpy.context.object
418         if obj and obj.mode == 'POSE':
419             return "active_pose_bone"
420         else:
421             return "active_bone"
422
423
424 classes = (
425     BONE_PT_context_bone,
426     BONE_PT_transform,
427     BONE_PT_curved,
428     BONE_PT_relations,
429     BONE_PT_display,
430     BONE_PT_inverse_kinematics,
431     BONE_PT_deform,
432     BONE_PT_custom_props,
433 )
434
435 if __name__ == "__main__":  # only for live edit.
436     from bpy.utils import register_class
437     for cls in classes:
438         register_class(cls)