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
134         if ob and bone:
135             pchan = ob.pose.bones[bone.name]
136             bbone = pchan
137         elif bone is None:
138             bone = context.edit_bone
139             bbone = bone
140         else:
141             bbone = bone
142
143         layout = self.layout
144         layout.use_property_split = True
145
146         layout.prop(bone, "bbone_segments", text="Segments")
147
148         topcol = layout.column()
149         topcol.active = bone.bbone_segments > 1
150
151         col = topcol.column(align=True)
152         col.prop(bbone, "bbone_curveinx", text="Curve In X")
153         col.prop(bbone, "bbone_curveiny", text="In Y")
154
155         col = topcol.column(align=True)
156         col.prop(bbone, "bbone_curveoutx", text="Curve Out X")
157         col.prop(bbone, "bbone_curveouty", text="Out Y")
158
159         col = topcol.column(align=True)
160         col.prop(bbone, "bbone_rollin", text="Roll In")
161         col.prop(bbone, "bbone_rollout", text="Out")
162         col.prop(bone, "use_endroll_as_inroll")
163
164         col = topcol.column(align=True)
165         col.prop(bbone, "bbone_scalein", text="Scale In")
166         col.prop(bbone, "bbone_scaleout", text="Out")
167
168         col = topcol.column(align=True)
169         col.prop(bbone, "bbone_easein", text="Ease In")
170         col.prop(bbone, "bbone_easeout", text="Out")
171
172         if pchan:
173             topcol.separator()
174
175             col = topcol.column()
176             col.use_property_split = False
177             col.prop(pchan, "use_bbone_custom_handles")
178
179             col = topcol.column(align=True)
180             col.active = pchan.use_bbone_custom_handles
181             col.use_property_split = True
182
183             sub = col.column()
184             sub.prop_search(pchan, "bbone_custom_handle_start", ob.pose, "bones", text="Custom Handle Start")
185             sub.prop_search(pchan, "bbone_custom_handle_end", ob.pose, "bones", text="End")
186
187             sub = col.column(align=True)
188             sub.prop(pchan, "use_bbone_relative_start_handle", text="Relative Handle Start")
189             sub.prop(pchan, "use_bbone_relative_end_handle", text="End")
190
191
192 class BONE_PT_relations(BoneButtonsPanel, Panel):
193     bl_label = "Relations"
194
195     def draw(self, context):
196         layout = self.layout
197         layout.use_property_split = True
198
199         ob = context.object
200         bone = context.bone
201         arm = context.armature
202         pchan = None
203
204         if ob and bone:
205             pchan = ob.pose.bones[bone.name]
206         elif bone is None:
207             bone = context.edit_bone
208
209         col = layout.column()
210         col.use_property_split = False
211         col.prop(bone, "layers", text="")
212         col.use_property_split = True
213         col = layout.column()
214
215         col.separator()
216
217         if context.bone:
218             col.prop(bone, "parent")
219         else:
220             col.prop_search(bone, "parent", arm, "edit_bones")
221
222         if ob and pchan:
223             col.prop(bone, "use_relative_parent")
224             col.prop_search(pchan, "bone_group", ob.pose, "bone_groups", text="Bone Group")
225
226         sub = col.column()
227         sub.active = (bone.parent is not None)
228         sub.prop(bone, "use_connect")
229         sub.prop(bone, "use_inherit_rotation")
230         sub.prop(bone, "use_inherit_scale")
231         sub = col.column()
232         sub.active = (not bone.parent or not bone.use_connect)
233         sub.prop(bone, "use_local_location")
234
235
236 class BONE_PT_display(BoneButtonsPanel, Panel):
237     bl_label = "Display"
238
239     @classmethod
240     def poll(cls, context):
241         return context.bone
242
243     def draw(self, context):
244         # note. this works ok in edit-mode but isn't
245         # all that useful so disabling for now.
246         layout = self.layout
247         layout.use_property_split = True
248
249         ob = context.object
250         bone = context.bone
251         pchan = None
252
253         if ob and bone:
254             pchan = ob.pose.bones[bone.name]
255         elif bone is None:
256             bone = context.edit_bone
257
258         if bone:
259
260             col = layout.column()
261             col.prop(bone, "hide", text="Hide")
262             sub = col.column()
263             sub.active = bool(pchan and pchan.custom_shape)
264             sub.prop(bone, "show_wire", text="Wireframe")
265
266             if pchan:
267                 col = layout.column()
268                 col.prop(pchan, "custom_shape")
269                 if pchan.custom_shape:
270                     col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size")
271                     col.prop(pchan, "custom_shape_scale", text="Scale")
272                     col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones")
273
274
275 class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
276     bl_label = "Inverse Kinematics"
277     bl_options = {'DEFAULT_CLOSED'}
278
279     @classmethod
280     def poll(cls, context):
281         ob = context.object
282         return ob and ob.mode == 'POSE' and context.bone
283
284     def draw(self, context):
285         layout = self.layout
286         layout.use_property_split = True
287
288         ob = context.object
289         bone = context.bone
290         pchan = ob.pose.bones[bone.name]
291
292         active = pchan.is_in_ik_chain
293
294         col = layout.column()
295         col.prop(pchan, "ik_stretch", slider=True)
296         col.active = active
297
298         layout.separator()
299
300         col = layout.column(align=True)
301
302         col.prop(pchan, "lock_ik_x", text="Lock IK X")
303         col.prop(pchan, "lock_ik_y", text="Y")
304         col.prop(pchan, "lock_ik_z", text="Z")
305
306         col = layout.column(align=True)
307
308         sub = col.column(align=True)
309         sub.active = pchan.lock_ik_x is False and active
310         sub.prop(pchan, "ik_stiffness_x", text="Stiffness X", slider=True)
311         sub = col.column(align=True)
312         sub.active = pchan.lock_ik_y is False and active
313         sub.prop(pchan, "ik_stiffness_y", text="Y", slider=True)
314         sub = col.column(align=True)
315         sub.active = pchan.lock_ik_z is False and active
316         sub.prop(pchan, "ik_stiffness_z", text="Z", slider=True)
317
318         col = layout.column(align=True)
319
320         sub = col.column()
321         sub.active = pchan.lock_ik_x is False and active
322         sub.prop(pchan, "use_ik_limit_x", text="Limit X")
323
324         sub = col.column(align=True)
325         sub.active = pchan.lock_ik_x is False and pchan.use_ik_limit_x and active
326         sub.prop(pchan, "ik_min_x", text="Min")
327         sub.prop(pchan, "ik_max_x", text="Max")
328
329         col.separator()
330
331         sub = col.column()
332         sub.active = pchan.lock_ik_y is False and active
333         sub.prop(pchan, "use_ik_limit_y", text="Limit Y")
334
335         sub = col.column(align=True)
336         sub.active = pchan.lock_ik_y is False and pchan.use_ik_limit_y and active
337         sub.prop(pchan, "ik_min_y", text="Min")
338         sub.prop(pchan, "ik_max_y", text="Max")
339
340         col.separator()
341
342         sub = col.column()
343         sub.active = pchan.lock_ik_z is False and active
344         sub.prop(pchan, "use_ik_limit_z", text="Limit Z")
345
346         sub = col.column(align=True)
347         sub.active = pchan.lock_ik_z is False and pchan.use_ik_limit_z and active
348         sub.prop(pchan, "ik_min_z", text="Min")
349         sub.prop(pchan, "ik_max_z", text="Max")
350
351         col.separator()
352
353         if ob.pose.ik_solver == 'ITASC':
354
355             col = layout.column()
356             col.prop(pchan, "use_ik_rotation_control", text="Control Rotation")
357             col.active = active
358
359             col = layout.column()
360
361             col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
362             col.active = active
363             # not supported yet
364             #row = layout.row()
365             #row.prop(pchan, "use_ik_linear_control", text="Joint Size")
366             #row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
367
368
369 class BONE_PT_deform(BoneButtonsPanel, Panel):
370     bl_label = "Deform"
371     bl_options = {'DEFAULT_CLOSED'}
372
373     def draw_header(self, context):
374         bone = context.bone
375
376         if not bone:
377             bone = context.edit_bone
378
379         self.layout.prop(bone, "use_deform", text="")
380
381     def draw(self, context):
382         layout = self.layout
383         layout.use_property_split = True
384
385         bone = context.bone
386
387         if not bone:
388             bone = context.edit_bone
389
390         layout.active = bone.use_deform
391
392         col = layout.column()
393         col.prop(bone, "envelope_distance", text="Envelope Distance")
394         col.prop(bone, "envelope_weight", text="Envelope Weight")
395         col.prop(bone, "use_envelope_multiply", text="Envelope Multiply")
396
397         col.separator()
398
399         col = layout.column(align=True)
400         col.prop(bone, "head_radius", text="Radius Head")
401         col.prop(bone, "tail_radius", text="Tail")
402
403
404 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
405     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
406     _property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
407
408     @property
409     def _context_path(self):
410         obj = bpy.context.object
411         if obj and obj.mode == 'POSE':
412             return "active_pose_bone"
413         else:
414             return "active_bone"
415
416
417 classes = (
418     BONE_PT_context_bone,
419     BONE_PT_transform,
420     BONE_PT_curved,
421     BONE_PT_relations,
422     BONE_PT_display,
423     BONE_PT_inverse_kinematics,
424     BONE_PT_deform,
425     BONE_PT_custom_props,
426 )
427
428 if __name__ == "__main__":  # only for live edit.
429     from bpy.utils import register_class
430     for cls in classes:
431         register_class(cls)