diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_constraint.py | 2076 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_constraint.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/constraint.c | 9 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_290.c | 15 | ||||
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_extra.c | 2 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 4 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_panel.c | 14 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 297 | ||||
-rw-r--r-- | source/blender/editors/object/object_constraint.c | 71 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_constraint_types.h | 7 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_constraint.c | 10 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui_api.c | 15 |
14 files changed, 1678 insertions, 847 deletions
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 3fc54ff6d12..04d43c28c27 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -20,894 +20,753 @@ from bpy.types import Panel -class ConstraintButtonsPanel: +class ObjectConstraintPanel(Panel): + bl_context = "constraint" + + @classmethod + def poll(cls, context): + return (context.object) + + +class BoneConstraintPanel(Panel): + bl_context = "bone_constraint" + + @classmethod + def poll(cls, context): + return (context.pose_bone) + + +class OBJECT_PT_constraints(ObjectConstraintPanel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' - bl_context = "constraint" + bl_label = "Object Constraints" + bl_options = {'HIDE_HEADER'} - def draw_constraint(self, context, con): + def draw(self, context): layout = self.layout - box = layout.template_constraint(con) + layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint") - if box: - # match enum type to our functions, avoids a lookup table. - getattr(self, con.type)(context, box, con) + layout.template_constraints(use_bone_constraints=False) - if con.type in {'RIGID_BODY_JOINT', 'NULL'}: - return - if con.type in {'IK', 'SPLINE_IK'}: - # constraint.disable_keep_transform doesn't work well - # for these constraints. - box.prop(con, "influence") - else: - row = box.row(align=True) - row.prop(con, "influence") - row.operator("constraint.disable_keep_transform", text="", icon='CANCEL') +class BONE_PT_constraints(BoneConstraintPanel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "Bone Constraints" + bl_options = {'HIDE_HEADER'} - @staticmethod - def space_template(layout, con, target=True, owner=True): - if target or owner: + def draw(self, context): + layout = self.layout - split = layout.split(factor=0.2) + layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint") - split.label(text="Space:") - row = split.row() + layout.template_constraints(use_bone_constraints=True) - if target: - row.prop(con, "target_space", text="") - if target and owner: - row.label(icon='ARROW_LEFTRIGHT') +# Parent class for constraint panels, with templates and drawing methods +# shared between the bone and object constraint panels +class ConstraintButtonsPanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "" + bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'} + @staticmethod + def draw_influence(layout, con): + layout.separator() + if con.type in {'IK', 'SPLINE_IK'}: + # constraint.disable_keep_transform doesn't work well + # for these constraints. + layout.prop(con, "influence") + else: + row = layout.row(align=True) + row.prop(con, "influence") + row.operator("constraint.disable_keep_transform", text="", icon='CANCEL') + + @staticmethod + def space_template(layout, con, target=True, owner=True): + if target or owner: + layout.separator() + if target: + layout.prop(con, "target_space", text="Target") if owner: - row.prop(con, "owner_space", text="") + layout.prop(con, "owner_space", text="Owner") @staticmethod def target_template(layout, con, subtargets=True): - layout.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object + col = layout.column() + col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object if con.target and subtargets: if con.target.type == 'ARMATURE': - layout.prop_search(con, "subtarget", con.target.data, "bones", text="Bone") + col.prop_search(con, "subtarget", con.target.data, "bones", text="Bone") if hasattr(con, "head_tail"): - row = layout.row(align=True) - row.label(text="Head/Tail:") + row = col.row(align=True, heading="Head/Tail") + row.use_property_decorate = False row.prop(con, "head_tail", text="") # XXX icon, and only when bone has segments? row.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER') elif con.target.type in {'MESH', 'LATTICE'}: - layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") + col.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") - @staticmethod - def ik_template(layout, con): - # only used for iTaSC - layout.prop(con, "pole_target") + def get_constraint(self, context): + con = None + if context.pose_bone: + con = context.pose_bone.constraints[self.list_panel_index] + else: + con = context.object.constraints[self.list_panel_index] + self.layout.context_pointer_set("constraint", con) + return con - if con.pole_target and con.pole_target.type == 'ARMATURE': - layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + def draw_header(self, context): + layout = self.layout + con = self.get_constraint(context) - if con.pole_target: - row = layout.row() - row.label() - row.prop(con, "pole_angle") + layout.template_constraint_header(con) - split = layout.split(factor=0.33) - col = split.column() - col.prop(con, "use_tail") - col.prop(con, "use_stretch") + # Drawing methods for specific constraints. (Shared by object and bone constraint panels) - col = split.column() - col.prop(con, "chain_count") + def draw_childof(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def CHILD_OF(self, _context, layout, con): self.target_template(layout, con) - split = layout.split() - - col = split.column() - col.label(text="Location:") - col.prop(con, "use_location_x", text="X") - col.prop(con, "use_location_y", text="Y") - col.prop(con, "use_location_z", text="Z") - - col = split.column() - col.label(text="Rotation:") - col.prop(con, "use_rotation_x", text="X") - col.prop(con, "use_rotation_y", text="Y") - col.prop(con, "use_rotation_z", text="Z") - - col = split.column() - col.label(text="Scale:") - col.prop(con, "use_scale_x", text="X") - col.prop(con, "use_scale_y", text="Y") - col.prop(con, "use_scale_z", text="Z") + row = layout.row(heading="Location") + row.use_property_decorate = False + row.prop(con, "use_location_x", text="X", toggle=True) + row.prop(con, "use_location_y", text="Y", toggle=True) + row.prop(con, "use_location_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Rotation") + row.use_property_decorate = False + row.prop(con, "use_rotation_x", text="X", toggle=True) + row.prop(con, "use_rotation_y", text="Y", toggle=True) + row.prop(con, "use_rotation_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Scale") + row.use_property_decorate = False + row.prop(con, "use_scale_x", text="X", toggle=True) + row.prop(con, "use_scale_y", text="Y", toggle=True) + row.prop(con, "use_scale_z", text="Z", toggle=True) + row.label(icon='BLANK1') row = layout.row() row.operator("constraint.childof_set_inverse") row.operator("constraint.childof_clear_inverse") - def TRACK_TO(self, _context, layout, con): - self.target_template(layout, con) - - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) - - row = layout.row() - row.prop(con, "up_axis", text="Up") - row.prop(con, "use_target_z") - - self.space_template(layout, con) - - def IK(self, context, layout, con): - if context.object.pose.ik_solver == 'ITASC': - layout.prop(con, "ik_type") - getattr(self, 'IK_' + con.ik_type)(context, layout, con) - else: - # Standard IK constraint - self.target_template(layout, con) - layout.prop(con, "pole_target") - - if con.pole_target and con.pole_target.type == 'ARMATURE': - layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") - - if con.pole_target: - row = layout.row() - row.prop(con, "pole_angle") - row.label() - - split = layout.split() - col = split.column() - col.prop(con, "iterations") - col.prop(con, "chain_count") - - col = split.column() - col.prop(con, "use_tail") - col.prop(con, "use_stretch") - - layout.label(text="Weight:") - - split = layout.split() - col = split.column() - row = col.row(align=True) - row.prop(con, "use_location", text="") - sub = row.row(align=True) - sub.active = con.use_location - sub.prop(con, "weight", text="Position", slider=True) - - col = split.column() - row = col.row(align=True) - row.prop(con, "use_rotation", text="") - sub = row.row(align=True) - sub.active = con.use_rotation - sub.prop(con, "orient_weight", text="Rotation", slider=True) + self.draw_influence(layout, con) - def IK_COPY_POSE(self, _context, layout, con): - self.target_template(layout, con) - self.ik_template(layout, con) + def draw_trackto(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - row = layout.row() - row.label(text="Axis Ref:") - row.prop(con, "reference_axis", expand=True) - split = layout.split(factor=0.33) - split.row().prop(con, "use_location") - row = split.row() - row.prop(con, "weight", text="Weight", slider=True) - row.active = con.use_location - split = layout.split(factor=0.33) - row = split.row() - row.label(text="Lock:") - row = split.row() - row.prop(con, "lock_location_x", text="X") - row.prop(con, "lock_location_y", text="Y") - row.prop(con, "lock_location_z", text="Z") - split.active = con.use_location - - split = layout.split(factor=0.33) - split.row().prop(con, "use_rotation") - row = split.row() - row.prop(con, "orient_weight", text="Weight", slider=True) - row.active = con.use_rotation - split = layout.split(factor=0.33) - row = split.row() - row.label(text="Lock:") - row = split.row() - row.prop(con, "lock_rotation_x", text="X") - row.prop(con, "lock_rotation_y", text="Y") - row.prop(con, "lock_rotation_z", text="Z") - split.active = con.use_rotation - - def IK_DISTANCE(self, _context, layout, con): self.target_template(layout, con) - self.ik_template(layout, con) - layout.prop(con, "limit_mode") + layout.prop(con, "track_axis", expand=True) + layout.prop(con, "up_axis", text="Up", expand=True) + layout.prop(con, "use_target_z") - row = layout.row() - row.prop(con, "weight", text="Weight", slider=True) - row.prop(con, "distance", text="Distance", slider=True) + self.space_template(layout, con) - def FOLLOW_PATH(self, _context, layout, con): - self.target_template(layout, con) - layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA') + self.draw_influence(layout, con) - split = layout.split() + def draw_follow_path(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - col = split.column() - col.prop(con, "use_curve_follow") - col.prop(con, "use_curve_radius") + self.target_template(layout, con) - col = split.column() - col.prop(con, "use_fixed_location") if con.use_fixed_location: - col.prop(con, "offset_factor", text="Offset") + layout.prop(con, "offset_factor", text="Offset Factor") else: - col.prop(con, "offset") + layout.prop(con, "offset") - row = layout.row() - row.label(text="Forward:") - row.prop(con, "forward_axis", expand=True) + layout.prop(con, "forward_axis", expand=True) + layout.prop(con, "up_axis", expand=True) - row = layout.row() - row.prop(con, "up_axis", text="Up") - row.label() + col = layout.column() + col.prop(con, "use_fixed_location") + col.prop(con, "use_curve_radius") + col.prop(con, "use_curve_follow") - def LIMIT_ROTATION(self, _context, layout, con): - split = layout.split() + layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA') - col = split.column(align=True) - col.prop(con, "use_limit_x") - sub = col.column(align=True) + self.draw_influence(layout, con) + + def draw_rot_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + # Decorators and property split are really buggy with these properties + row = layout.row(heading="Limit X", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_x", text="") + sub = row.column(align=True) sub.active = con.use_limit_x sub.prop(con, "min_x", text="Min") sub.prop(con, "max_x", text="Max") + row.label(icon="BLANK1") - col = split.column(align=True) - col.prop(con, "use_limit_y") - sub = col.column(align=True) + row = layout.row(heading="Y", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_y", text="") + sub = row.column(align=True) sub.active = con.use_limit_y sub.prop(con, "min_y", text="Min") sub.prop(con, "max_y", text="Max") + row.label(icon="BLANK1") - col = split.column(align=True) - col.prop(con, "use_limit_z") - sub = col.column(align=True) + row = layout.row(heading="Z", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_z", text="") + sub = row.column(align=True) sub.active = con.use_limit_z sub.prop(con, "min_z", text="Min") sub.prop(con, "max_z", text="Max") + row.label(icon="BLANK1") layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def LIMIT_LOCATION(self, _context, layout, con): - split = layout.split() - - col = split.column() - col.prop(con, "use_min_x") - sub = col.column() - sub.active = con.use_min_x - sub.prop(con, "min_x", text="") - col.prop(con, "use_max_x") - sub = col.column() - sub.active = con.use_max_x - sub.prop(con, "max_x", text="") - - col = split.column() - col.prop(con, "use_min_y") - sub = col.column() - sub.active = con.use_min_y - sub.prop(con, "min_y", text="") - col.prop(con, "use_max_y") - sub = col.column() - sub.active = con.use_max_y - sub.prop(con, "max_y", text="") - - col = split.column() - col.prop(con, "use_min_z") - sub = col.column() - sub.active = con.use_min_z - sub.prop(con, "min_z", text="") - col.prop(con, "use_max_z") - sub = col.column() - sub.active = con.use_max_z - sub.prop(con, "max_z", text="") + self.draw_influence(layout, con) - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + def draw_loc_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def LIMIT_SCALE(self, _context, layout, con): - split = layout.split() - - col = split.column() - col.prop(con, "use_min_x") - sub = col.column() - sub.active = con.use_min_x - sub.prop(con, "min_x", text="") - col.prop(con, "use_max_x") - sub = col.column() - sub.active = con.use_max_x - sub.prop(con, "max_x", text="") - - col = split.column() - col.prop(con, "use_min_y") - sub = col.column() - sub.active = con.use_min_y - sub.prop(con, "min_y", text="") - col.prop(con, "use_max_y") - sub = col.column() - sub.active = con.use_max_y - sub.prop(con, "max_y", text="") - - col = split.column() - col.prop(con, "use_min_z") - sub = col.column() - sub.active = con.use_min_z - sub.prop(con, "min_z", text="") - col.prop(con, "use_max_z") - sub = col.column() - sub.active = con.use_max_z - sub.prop(con, "max_z", text="") + col = layout.column() - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + row = col.row(heading="Minimum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_x + subsub.prop(con, "min_x", text="") + row.prop_decorator(con, "min_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_y + subsub.prop(con, "min_y", text="") + row.prop_decorator(con, "min_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_z + subsub.prop(con, "min_z", text="") + row.prop_decorator(con, "min_z") + + col.separator() + + row = col.row(heading="Maximum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_x + subsub.prop(con, "max_x", text="") + row.prop_decorator(con, "max_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_y + subsub.prop(con, "max_y", text="") + row.prop_decorator(con, "max_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_z + subsub.prop(con, "max_z", text="") + row.prop_decorator(con, "max_z") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") + layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - def COPY_ROTATION(self, _context, layout, con): - self.target_template(layout, con) + self.draw_influence(layout, con) - layout.prop(con, "euler_order", text="Order") + def draw_size_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + col = layout.column() + + row = col.row(heading="Minimum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_x + subsub.prop(con, "min_x", text="") + row.prop_decorator(con, "min_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_y + subsub.prop(con, "min_y", text="") + row.prop_decorator(con, "min_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_z + subsub.prop(con, "min_z", text="") + row.prop_decorator(con, "min_z") + + col.separator() + + row = col.row(heading="Maximum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_x + subsub.prop(con, "max_x", text="") + row.prop_decorator(con, "max_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_y + subsub.prop(con, "max_y", text="") + row.prop_decorator(con, "max_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_z + subsub.prop(con, "max_z", text="") + row.prop_decorator(con, "max_z") - split = layout.split() - col = split.column() - col.prop(con, "use_x", text="X") - sub = col.column() - sub.active = con.use_x - sub.prop(con, "invert_x", text="Invert") + layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - col = split.column() - col.prop(con, "use_y", text="Y") - sub = col.column() - sub.active = con.use_y - sub.prop(con, "invert_y", text="Invert") + self.draw_influence(layout, con) - col = split.column() - col.prop(con, "use_z", text="Z") - sub = col.column() - sub.active = con.use_z - sub.prop(con, "invert_z", text="Invert") + def draw_rotate_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + self.target_template(layout, con) + + layout.prop(con, "euler_order", text="Order") + + row = layout.row(heading="Axis") + row.use_property_decorate = False + row.prop(con, "use_x", text="X", toggle=True) + row.prop(con, "use_y", text="Y", toggle=True) + row.prop(con, "use_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Invert") + row.use_property_decorate = False + row.prop(con, "invert_x", text="X", toggle=True) + row.prop(con, "invert_y", text="Y", toggle=True) + row.prop(con, "invert_z", text="Z", toggle=True) + row.label(icon='BLANK1') layout.prop(con, "mix_mode", text="Mix") self.space_template(layout, con) - def COPY_LOCATION(self, _context, layout, con): - self.target_template(layout, con) + self.draw_influence(layout, con) - split = layout.split() + def draw_locate_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - col = split.column() - col.prop(con, "use_x", text="X") - sub = col.column() - sub.active = con.use_x - sub.prop(con, "invert_x", text="Invert") + self.target_template(layout, con) - col = split.column() - col.prop(con, "use_y", text="Y") - sub = col.column() - sub.active = con.use_y - sub.prop(con, "invert_y", text="Invert") + row = layout.row(heading="Axis") + row.use_property_decorate = False + row.prop(con, "use_x", text="X", toggle=True) + row.prop(con, "use_y", text="Y", toggle=True) + row.prop(con, "use_z", text="Z", toggle=True) + row.label(icon='BLANK1') - col = split.column() - col.prop(con, "use_z", text="Z") - sub = col.column() - sub.active = con.use_z - sub.prop(con, "invert_z", text="Invert") + row = layout.row(heading="Invert") + row.use_property_decorate = False + row.prop(con, "invert_x", text="X", toggle=True) + row.prop(con, "invert_y", text="Y", toggle=True) + row.prop(con, "invert_z", text="Z", toggle=True) + row.label(icon='BLANK1') layout.prop(con, "use_offset") self.space_template(layout, con) - def COPY_SCALE(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_size_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + self.target_template(layout, con) - row = layout.row(align=True) - row.prop(con, "use_x", text="X") - row.prop(con, "use_y", text="Y") - row.prop(con, "use_z", text="Z") + row = layout.row(heading="Axis") + row.prop(con, "use_x", text="X", toggle=True) + row.prop(con, "use_y", text="Y", toggle=True) + row.prop(con, "use_z", text="Z", toggle=True) - layout.prop(con, "power") - layout.prop(con, "use_make_uniform") + col = layout.column() + col.prop(con, "power") + col.prop(con, "use_make_uniform") - row = layout.row() - row.prop(con, "use_offset") - row = row.row() + col.prop(con, "use_offset") + row = col.row() row.active = con.use_offset row.prop(con, "use_add") self.space_template(layout, con) - def MAINTAIN_VOLUME(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_same_volume(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True layout.prop(con, "mode") - row = layout.row() - row.label(text="Free:") + row = layout.row(heading="Free Axis") row.prop(con, "free_axis", expand=True) layout.prop(con, "volume") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def COPY_TRANSFORMS(self, _context, layout, con): - self.target_template(layout, con) + layout.prop(con, "owner_space") - layout.prop(con, "mix_mode", text="Mix") - - self.space_template(layout, con) + self.draw_influence(layout, con) - # def SCRIPT(self, context, layout, con): + def draw_trans_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def ACTION(self, _context, layout, con): self.target_template(layout, con) - split = layout.split() - - col = split.column() - col.label(text="From Target:") - col.prop(con, "transform_channel", text="") - col.prop(con, "target_space", text="") - - col = split.column() - col.label(text="To Action:") - col.prop(con, "action", text="") - col.prop(con, "use_bone_object_action") - - split = layout.split() - - col = split.column(align=True) - col.label(text="Target Range:") - col.prop(con, "min", text="Min") - col.prop(con, "max", text="Max") - - col = split.column(align=True) - col.label(text="Action Range:") - col.prop(con, "frame_start", text="Start") - col.prop(con, "frame_end", text="End") - layout.prop(con, "mix_mode", text="Mix") - def LOCKED_TRACK(self, _context, layout, con): - self.target_template(layout, con) + self.space_template(layout, con) - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) + self.draw_influence(layout, con) - row = layout.row() - row.label(text="Lock:") - row.prop(con, "lock_axis", expand=True) + def draw_action(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def LIMIT_DISTANCE(self, _context, layout, con): self.target_template(layout, con) - col = layout.column(align=True) - col.prop(con, "distance") - col.operator("constraint.limitdistance_reset") - - row = layout.row() - row.label(text="Clamp Region:") - row.prop(con, "limit_mode", text="") + layout.prop(con, "mix_mode", text="Mix") - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + self.draw_influence(layout, con) - self.space_template(layout, con) + def draw_lock_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def STRETCH_TO(self, _context, layout, con): self.target_template(layout, con) - row = layout.row() - row.prop(con, "rest_length", text="Rest Length") - row.operator("constraint.stretchto_reset", text="Reset") - - layout.prop(con, "bulge", text="Volume Variation") - split = layout.split() - col = split.column(align=True) - col.prop(con, "use_bulge_min", text="Volume Min") - sub = col.column() - sub.active = con.use_bulge_min - sub.prop(con, "bulge_min", text="") - col = split.column(align=True) - col.prop(con, "use_bulge_max", text="Volume Max") - sub = col.column() - sub.active = con.use_bulge_max - sub.prop(con, "bulge_max", text="") - col = layout.column() - col.active = con.use_bulge_min or con.use_bulge_max - col.prop(con, "bulge_smooth", text="Smooth") + layout.prop(con, "track_axis", expand=True) + layout.prop(con, "lock_axis", expand=True) - split = layout.split(factor=0.3) - split.label(text="Volume:") - row = split.row() - row.prop(con, "volume", expand=True) + self.draw_influence(layout, con) - split = layout.split(factor=0.3) - split.label(text="Rotation:") - row = split.row() - row.prop(con, "keep_axis", expand=True) + def draw_dist_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def FLOOR(self, _context, layout, con): self.target_template(layout, con) - layout.prop(con, "use_rotation") - layout.prop(con, "offset") - row = layout.row() - row.label(text="Min/Max:") - row.prop(con, "floor_location", expand=True) + row.prop(con, "distance") + row.operator("constraint.limitdistance_reset", text="", icon="X") - self.space_template(layout, con) - - def RIGID_BODY_JOINT(self, _context, layout, con): - self.target_template(layout, con, subtargets=False) + layout.prop(con, "limit_mode", text="Clamp Region") - layout.prop(con, "pivot_type") - layout.prop(con, "child") + layout.prop(con, "use_transform_limit") - row = layout.row() - row.prop(con, "use_linked_collision", text="Linked Collision") - row.prop(con, "show_pivot", text="Display Pivot") - - split = layout.split() - - col = split.column(align=True) - col.label(text="Pivot:") - col.prop(con, "pivot_x", text="X") - col.prop(con, "pivot_y", text="Y") - col.prop(con, "pivot_z", text="Z") - - col = split.column(align=True) - col.label(text="Axis:") - col.prop(con, "axis_x", text="X") - col.prop(con, "axis_y", text="Y") - col.prop(con, "axis_z", text="Z") - - if con.pivot_type == 'CONE_TWIST': - layout.label(text="Limits:") - split = layout.split() - - col = split.column() - col.prop(con, "use_angular_limit_x", text="Angle X") - sub = col.column() - sub.active = con.use_angular_limit_x - sub.prop(con, "limit_angle_max_x", text="") - - col = split.column() - col.prop(con, "use_angular_limit_y", text="Angle Y") - sub = col.column() - sub.active = con.use_angular_limit_y - sub.prop(con, "limit_angle_max_y", text="") - - col = split.column() - col.prop(con, "use_angular_limit_z", text="Angle Z") - sub = col.column() - sub.active = con.use_angular_limit_z - sub.prop(con, "limit_angle_max_z", text="") - - elif con.pivot_type == 'GENERIC_6_DOF': - layout.label(text="Limits:") - split = layout.split() - - col = split.column(align=True) - col.prop(con, "use_limit_x", text="X") - sub = col.column(align=True) - sub.active = con.use_limit_x - sub.prop(con, "limit_min_x", text="Min") - sub.prop(con, "limit_max_x", text="Max") - - col = split.column(align=True) - col.prop(con, "use_limit_y", text="Y") - sub = col.column(align=True) - sub.active = con.use_limit_y - sub.prop(con, "limit_min_y", text="Min") - sub.prop(con, "limit_max_y", text="Max") - - col = split.column(align=True) - col.prop(con, "use_limit_z", text="Z") - sub = col.column(align=True) - sub.active = con.use_limit_z - sub.prop(con, "limit_min_z", text="Min") - sub.prop(con, "limit_max_z", text="Max") - - split = layout.split() - - col = split.column(align=True) - col.prop(con, "use_angular_limit_x", text="Angle X") - sub = col.column(align=True) - sub.active = con.use_angular_limit_x - sub.prop(con, "limit_angle_min_x", text="Min") - sub.prop(con, "limit_angle_max_x", text="Max") - - col = split.column(align=True) - col.prop(con, "use_angular_limit_y", text="Angle Y") - sub = col.column(align=True) - sub.active = con.use_angular_limit_y - sub.prop(con, "limit_angle_min_y", text="Min") - sub.prop(con, "limit_angle_max_y", text="Max") - - col = split.column(align=True) - col.prop(con, "use_angular_limit_z", text="Angle Z") - sub = col.column(align=True) - sub.active = con.use_angular_limit_z - sub.prop(con, "limit_angle_min_z", text="Min") - sub.prop(con, "limit_angle_max_z", text="Max") - - elif con.pivot_type == 'HINGE': - layout.label(text="Limits:") - split = layout.split() + self.space_template(layout, con) - row = split.row(align=True) - col = row.column() - col.prop(con, "use_angular_limit_x", text="Angle X") + self.draw_influence(layout, con) - col = row.column() - col.active = con.use_angular_limit_x - col.prop(con, "limit_angle_min_x", text="Min") - col = row.column() - col.active = con.use_angular_limit_x - col.prop(con, "limit_angle_max_x", text="Max") + def draw_stretch_to(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def CLAMP_TO(self, _context, layout, con): self.target_template(layout, con) row = layout.row() - row.label(text="Main Axis:") - row.prop(con, "main_axis", expand=True) - - layout.prop(con, "use_cyclic") + row.prop(con, "rest_length") + row.operator("constraint.stretchto_reset", text="", icon="X") - def TRANSFORM(self, _context, layout, con): - self.target_template(layout, con) - - layout.prop(con, "use_motion_extrapolate", text="Extrapolate") + layout.separator() col = layout.column() - col.row().label(text="Source:") - col.row().prop(con, "map_from", expand=True) + col.prop(con, "bulge", text="Volume Variation") + + row = col.row(heading="Volume Min", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_bulge_min", text="") + subsub = sub.row(align=True) + subsub.active = con.use_bulge_min + subsub.prop(con, "bulge_min", text="") + row.prop_decorator(con, "bulge_min") + + row = col.row(heading="Max", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_bulge_max", text="") + subsub = sub.row(align=True) + subsub.active = con.use_bulge_max + subsub.prop(con, "bulge_max", text="") + row.prop_decorator(con, "bulge_max") - if con.map_from == 'ROTATION': - layout.prop(con, "from_rotation_mode", text="Mode") - - split = layout.split() - ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale" - - sub = split.column(align=True) - sub.label(text="X:") - sub.prop(con, "from_min_x" + ext, text="Min") - sub.prop(con, "from_max_x" + ext, text="Max") + row = col.row() + row.active = con.use_bulge_min or con.use_bulge_max + row.prop(con, "bulge_smooth", text="Smooth") - sub = split.column(align=True) - sub.label(text="Y:") - sub.prop(con, "from_min_y" + ext, text="Min") - sub.prop(con, "from_max_y" + ext, text="Max") + layout.prop(con, "volume", expand=True) + layout.prop(con, "keep_axis", text="Rotation", expand=True) - sub = split.column(align=True) - sub.label(text="Z:") - sub.prop(con, "from_min_z" + ext, text="Min") - sub.prop(con, "from_max_z" + ext, text="Max") + self.draw_influence(layout, con) - col = layout.column() - row = col.row() - row.label(text="Source to Destination Mapping:") + def draw_min_max(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - # note: chr(187) is the ASCII arrow ( >> ). Blender Text Editor can't - # open it. Thus we are using the hard-coded value instead. - row = col.row() - row.prop(con, "map_to_x_from", expand=False, text="") - row.label(text=" %s X" % chr(187)) + self.target_template(layout, con) - row = col.row() - row.prop(con, "map_to_y_from", expand=False, text="") - row.label(text=" %s Y" % chr(187)) + layout.prop(con, "offset") + layout.prop(con, "floor_location", expand=True, text="Min/Max") + layout.prop(con, "use_rotation") - row = col.row() - row.prop(con, "map_to_z_from", expand=False, text="") - row.label(text=" %s Z" % chr(187)) + self.space_template(layout, con) - split = layout.split() + self.draw_influence(layout, con) - col = split.column() - col.label(text="Destination:") - col.row().prop(con, "map_to", expand=True) + def draw_clamp_to(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if con.map_to == 'ROTATION': - layout.prop(con, "to_euler_order", text="Order") + self.target_template(layout, con) - split = layout.split() - ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale" + layout.prop(con, "main_axis", expand=True) - col = split.column() - col.label(text="X:") + layout.prop(con, "use_cyclic") - sub = col.column(align=True) - sub.prop(con, "to_min_x" + ext, text="Min") - sub.prop(con, "to_max_x" + ext, text="Max") + self.draw_influence(layout, con) - col = split.column() - col.label(text="Y:") + def draw_transform(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - sub = col.column(align=True) - sub.prop(con, "to_min_y" + ext, text="Min") - sub.prop(con, "to_max_y" + ext, text="Max") + self.target_template(layout, con) - col = split.column() - col.label(text="Z:") + layout.prop(con, "use_motion_extrapolate", text="Extrapolate") - sub = col.column(align=True) - sub.prop(con, "to_min_z" + ext, text="Min") - sub.prop(con, "to_max_z" + ext, text="Max") + self.space_template(layout, con) - layout.prop(con, "mix_mode" + ext, text="Mix") + self.draw_influence(layout, con) - self.space_template(layout, con) + def draw_shrinkwrap(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def SHRINKWRAP(self, _context, layout, con): self.target_template(layout, con, False) layout.prop(con, "distance") - layout.prop(con, "shrinkwrap_type") + layout.prop(con, "shrinkwrap_type", text="Mode") - if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: - layout.prop(con, "wrap_mode", text="Snap Mode") + layout.separator() if con.shrinkwrap_type == 'PROJECT': - row = layout.row(align=True) - row.prop(con, "project_axis", expand=True) - split = layout.split(factor=0.4) - split.label(text="Axis Space:") - rowsub = split.row() - rowsub.prop(con, "project_axis_space", text="") - split = layout.split(factor=0.4) - split.label(text="Face Culling:") - rowsub = split.row() - rowsub.prop(con, "cull_face", expand=True) - row = layout.row() - row.prop(con, "use_project_opposite") - rowsub = row.row() - rowsub.active = con.use_project_opposite and con.cull_face != 'OFF' - rowsub.prop(con, "use_invert_cull") - layout.prop(con, "project_limit") + layout.prop(con, "project_axis", expand=True, text="Project Axis") + layout.prop(con, "project_axis_space", text="Space") + layout.prop(con, "project_limit", text="Distance") + layout.prop(con, "use_project_opposite") - if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: - layout.prop(con, "use_track_normal") + layout.separator() - row = layout.row(align=True) - row.active = con.use_track_normal - row.prop(con, "track_axis", expand=True) + col = layout.column() + row = col.row() + row.prop(con, "cull_face", expand=True) + row = col.row() + row.active = con.use_project_opposite and con.cull_face != 'OFF' + row.prop(con, "use_invert_cull") - def DAMPED_TRACK(self, _context, layout, con): - self.target_template(layout, con) + layout.separator() - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) + if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: + layout.prop(con, "wrap_mode", text="Snap Mode") + row = layout.row(heading="Align to Normal", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_track_normal", text="") + subsub = sub.row(align=True) + subsub.active = con.use_track_normal + subsub.prop(con, "track_axis", text="") + row.prop_decorator(con, "track_axis") + + self.draw_influence(layout, con) + + def draw_damp_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def SPLINE_IK(self, _context, layout, con): self.target_template(layout, con) - col = layout.column() - col.label(text="Spline Fitting:") - col.prop(con, "chain_count") - col.prop(con, "use_even_divisions") - col.prop(con, "use_chain_offset") + layout.prop(con, "track_axis", expand=True) - col = layout.column() - col.label(text="Chain Scaling:") - col.prop(con, "use_curve_radius") + self.draw_influence(layout, con) - layout.prop(con, "y_scale_mode") - layout.prop(con, "xz_scale_mode") + def draw_spline_ik(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}: - layout.prop(con, "use_original_scale") + self.target_template(layout, con) - if con.xz_scale_mode == 'VOLUME_PRESERVE': - layout.prop(con, "bulge", text="Volume Variation") - split = layout.split() - col = split.column(align=True) - col.prop(con, "use_bulge_min", text="Volume Min") - sub = col.column() - sub.active = con.use_bulge_min - sub.prop(con, "bulge_min", text="") - col = split.column(align=True) - col.prop(con, "use_bulge_max", text="Volume Max") - sub = col.column() - sub.active = con.use_bulge_max - sub.prop(con, "bulge_max", text="") - col = layout.column() - col.active = con.use_bulge_min or con.use_bulge_max - col.prop(con, "bulge_smooth", text="Smooth") + self.draw_influence(layout, con) + + def draw_pivot(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def PIVOT(self, _context, layout, con): self.target_template(layout, con) if con.target: - col = layout.column() - col.prop(con, "offset", text="Pivot Offset") + layout.prop(con, "offset", text="Pivot Offset") else: - col = layout.column() - col.prop(con, "use_relative_location") + layout.prop(con, "use_relative_location") if con.use_relative_location: - col.prop(con, "offset", text="Relative Pivot Point") + layout.prop(con, "offset", text="Pivot Point") else: - col.prop(con, "offset", text="Absolute Pivot Point") + layout.prop(con, "offset", text="Pivot Point") col = layout.column() - col.prop(con, "rotation_range", text="Pivot When") + col.prop(con, "rotation_range", text="Rotation Range") - @staticmethod - def _getConstraintClip(context, con): - if not con.use_active_clip: - return con.clip + self.draw_influence(layout, con) + + def draw_follow_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + clip = None + if con.use_active_clip: + clip = context.scene.active_clip else: - return context.scene.active_clip + clip = con.clip - def FOLLOW_TRACK(self, context, layout, con): - clip = self._getConstraintClip(context, con) + layout.prop(con, "use_active_clip") + layout.prop(con, "use_3d_position") row = layout.row() - row.prop(con, "use_active_clip") - row.prop(con, "use_3d_position") - - sub = row.column() - sub.active = not con.use_3d_position - sub.prop(con, "use_undistorted_position") + row.active = not con.use_3d_position + row.prop(con, "use_undistorted_position") - col = layout.column() if not con.use_active_clip: - col.prop(con, "clip") + layout.prop(con, "clip") - row = col.row() - row.prop(con, "frame_method", expand=True) + layout.prop(con, "frame_method") if clip: tracking = clip.tracking - col.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA') + layout.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA') tracking_object = tracking.objects.get(con.object, tracking.objects[0]) - col.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA') + layout.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA') - col.prop(con, "camera") + layout.prop(con, "camera") - row = col.row() + row = layout.row() row.active = not con.use_3d_position row.prop(con, "depth_object") layout.operator("clip.constraint_to_fcurve") - def CAMERA_SOLVER(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_camera_solver(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + layout.prop(con, "use_active_clip") if not con.use_active_clip: @@ -915,8 +774,19 @@ class ConstraintButtonsPanel: layout.operator("clip.constraint_to_fcurve") - def OBJECT_SOLVER(self, context, layout, con): - clip = self._getConstraintClip(context, con) + self.draw_influence(layout, con) + + def draw_object_solver(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + clip = None + if con.use_active_clip: + clip = context.scene.active_clip + else: + clip = con.clip layout.prop(con, "use_active_clip") @@ -934,36 +804,236 @@ class ConstraintButtonsPanel: layout.operator("clip.constraint_to_fcurve") - def TRANSFORM_CACHE(self, _context, layout, con): - layout.label(text="Cache File Properties:") - box = layout.box() - box.template_cache_file(con, "cache_file") + self.draw_influence(layout, con) - cache_file = con.cache_file + def draw_transform_cache(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + layout.template_cache_file(con, "cache_file") - layout.label(text="Constraint Properties:") - box = layout.box() + cache_file = con.cache_file if cache_file is not None: - box.prop_search(con, "object_path", cache_file, "object_paths") + layout.prop_search(con, "object_path", cache_file, "object_paths") - def SCRIPT(self, _context, layout, _con): + self.draw_influence(layout, con) + + def draw_python_constraint(self, context): + layout = self.layout layout.label(text="Blender 2.6 doesn't support python constraints yet") - def ARMATURE(self, context, layout, con): - topcol = layout.column() - topcol.use_property_split = True - topcol.operator("constraint.add_target", text="Add Target Bone") + def draw_armature(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + col = layout.column() + col.prop(con, "use_deform_preserve_volume") + col.prop(con, "use_bone_envelopes") + + if context.pose_bone: + col.prop(con, "use_current_location") + + layout.operator("constraint.add_target", text="Add Target Bone") + + layout.operator("constraint.normalize_target_weights") + + self.draw_influence(layout, con) if not con.targets: - box = topcol.box() - box.label(text="No target bones were added", icon='ERROR') + layout.label(text="No target bones added", icon='ERROR') - for i, tgt in enumerate(con.targets): - box = topcol.box() + def draw_kinematic(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + self.target_template(layout, con) + + if context.object.pose.ik_solver == 'ITASC': + layout.prop(con, "ik_type") + + layout.prop(con, "pole_target") + + if con.pole_target and con.pole_target.type == 'ARMATURE': + layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + + if con.pole_target: + row = layout.row() + row.label() + row.prop(con, "pole_angle") + + col = layout.column() + col.prop(con, "use_tail") + col.prop(con, "use_stretch") + col.prop(con, "chain_count") + + if con.ik_type == 'COPY_POSE': + layout.prop(con, "reference_axis", expand=True) + + col = layout.column() + col.prop(con, "use_location") + + sub = col.column() + sub.active = con.use_location + sub.prop(con, "weight", text="Weight", slider=True) + row = sub.row(heading="Lock") + row.use_property_decorate = False + row.prop(con, "lock_location_x", text="X", toggle=True) + row.prop(con, "lock_location_y", text="Y", toggle=True) + row.prop(con, "lock_location_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + col = layout.column() + col.prop(con, "use_rotation") + + sub = col.column() + sub.active = con.use_rotation + sub.prop(con, "orient_weight", text="Weight", slider=True) + row = sub.row(heading="Lock") + row.use_property_decorate = False + row.prop(con, "lock_rotation_x", text="X", toggle=True) + row.prop(con, "lock_rotation_y", text="Y", toggle=True) + row.prop(con, "lock_rotation_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + elif con.ik_type == 'DISTANCE': + layout.prop(con, "limit_mode") + + col = layout.column() + col.prop(con, "weight", text="Weight", slider=True) + col.prop(con, "distance", text="Distance", slider=True) + else: + # Standard IK constraint + layout.prop(con, "pole_target") + + if con.pole_target and con.pole_target.type == 'ARMATURE': + layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + + if con.pole_target: + row = layout.row() + row.prop(con, "pole_angle") + row.label() + + col = layout.column() + col.prop(con, "iterations") + col.prop(con, "chain_count") + col.prop(con, "use_tail") + col.prop(con, "use_stretch") + + col = layout.column() + row = col.row(align=True, heading="Weight Position") + row.prop(con, "use_location", text="") + sub = row.row(align=True) + sub.active = con.use_location + sub.prop(con, "weight", text="", slider=True) + + row = col.row(align=True, heading="Rotation") + row.prop(con, "use_rotation", text="") + sub = row.row(align=True) + sub.active = con.use_rotation + sub.prop(con, "orient_weight", text="", slider=True) + + self.draw_influence(layout, con) + +# Parent class for constraint subpanels +class ConstraintButtonsSubPanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "" + bl_options = {'DRAW_BOX'} + + def get_constraint(self, context): + con = None + if context.pose_bone: + con = context.pose_bone.constraints[self.list_panel_index] + else: + con = context.active.constraints[self.list_panel_index] + self.layout.context_pointer_set("constraint", con) + return con + + def draw_transform_source(self, context): + layout = self.layout + con = self.get_constraint(context) + + layout.prop(con, "map_from", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = True + + if con.map_from == 'ROTATION': + layout.prop(con, "from_rotation_mode", text="Mode") + + ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale" + + col = layout.column(align=True) + col.prop(con, "from_min_x" + ext, text="X Min") + col.prop(con, "from_max_x" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "from_min_y" + ext, text="Y Min") + col.prop(con, "from_max_y" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "from_min_z" + ext, text="Z Min") + col.prop(con, "from_max_z" + ext, text="Max") + + def draw_transform_mapping(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + layout.prop(con, "map_to_x_from", expand=False, text="Source Axis X") + + layout.prop(con, "map_to_y_from", expand=False, text="Y") + + layout.prop(con, "map_to_z_from", expand=False, text="Z") + + def draw_transform_destination(self, context): + layout = self.layout + con = self.get_constraint(context) + + layout.prop(con, "map_to", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = True + + if con.map_to == 'ROTATION': + layout.prop(con, "to_euler_order", text="Order") + + ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale" + + col = layout.column(align=True) + col.prop(con, "to_min_x" + ext, text="X Min") + col.prop(con, "to_max_x" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "to_min_y" + ext, text="Y Min") + col.prop(con, "to_max_y" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "to_min_z" + ext, text="Z Min") + col.prop(con, "to_max_z" + ext, text="Max") + + layout.prop(con, "mix_mode" + ext, text="Mix") + + def draw_armature_bones(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + for i, tgt in enumerate(con.targets): has_target = tgt.target is not None + box = layout.box() header = box.row() header.use_property_split = False @@ -977,61 +1047,615 @@ class ConstraintButtonsPanel: else: row.prop(tgt, "subtarget", text="", icon='BONE_DATA') - header.operator("constraint.remove_target", text="", icon='REMOVE').index = i + header.operator("constraint.remove_target", text="", icon='X').index = i - col = box.column() - col.active = has_target and tgt.subtarget != "" - col.prop(tgt, "weight", slider=True) + row = box.row() + row.active = has_target and tgt.subtarget != "" + row.prop(tgt, "weight", slider=True, text="Weight") - topcol.operator("constraint.normalize_target_weights") - topcol.prop(con, "use_deform_preserve_volume") - topcol.prop(con, "use_bone_envelopes") + def draw_spline_ik_fitting(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if context.pose_bone: - topcol.prop(con, "use_current_location") + col = layout.column() + col.prop(con, "chain_count") + col.prop(con, "use_even_divisions") + col.prop(con, "use_chain_offset") + def draw_spline_ik_chain_scaling(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True -class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel): - bl_label = "Object Constraints" - bl_context = "constraint" - bl_options = {'HIDE_HEADER'} + layout.prop(con, "use_curve_radius") - @classmethod - def poll(cls, context): - return (context.object) + layout.prop(con, "y_scale_mode") + layout.prop(con, "xz_scale_mode") - def draw(self, context): + if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}: + layout.prop(con, "use_original_scale") + + if con.xz_scale_mode == 'VOLUME_PRESERVE': + col = layout.column() + col.prop(con, "bulge", text="Volume Variation") + + row = col.row(heading="Volume Min") + row.prop(con, "use_bulge_min", text="") + sub = row.row() + sub.active = con.use_bulge_min + sub.prop(con, "bulge_min", text="") + + row = col.row(heading="Max") + row.prop(con, "use_bulge_max", text="") + sub = row.row() + sub.active = con.use_bulge_max + sub.prop(con, "bulge_max", text="") + + row = layout.row() + row.active = con.use_bulge_min or con.use_bulge_max + row.prop(con, "bulge_smooth", text="Smooth") + + def draw_action_target(self, context): layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - obj = context.object + layout.prop(con, "transform_channel", text="Channel") + layout.prop(con, "target_space") - layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint") + col = layout.column(align=True) + col.prop(con, "min", text="Range Min") + col.prop(con, "max", text="Max") - for con in obj.constraints: - self.draw_constraint(context, con) + def draw_action_action(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True -class BONE_PT_constraints(ConstraintButtonsPanel, Panel): - bl_label = "Bone Constraints" - bl_context = "bone_constraint" - bl_options = {'HIDE_HEADER'} + layout.prop(con, "action") + layout.prop(con, "use_bone_object_action") - @classmethod - def poll(cls, context): - return (context.pose_bone) + col = layout.column(align=True) + col.prop(con, "frame_start", text="Frame Start") + col.prop(con, "frame_end", text="End") + +# Child Of Constraint + +class OBJECT_PT_bChildOfConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): def draw(self, context): - layout = self.layout + self.draw_childof(context) - layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint") - for con in context.pose_bone.constraints: - self.draw_constraint(context, con) +class BONE_PT_bChildOfConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_childof(context) + +# Track To Constraint + +class OBJECT_PT_bTrackToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trackto(context) + + +class BONE_PT_bTrackToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trackto(context) + +# Follow Path Constraint + +class OBJECT_PT_bFollowPathConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_path(context) + + +class BONE_PT_bFollowPathConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_path(context) + + +# Roation Limit Constraint + +class OBJECT_PT_bRotLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rot_limit(context) + + +class BONE_PT_bRotLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rot_limit(context) + + +# Location Limit Constraint + +class OBJECT_PT_bLocLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_loc_limit(context) + + +class BONE_PT_bLocLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_loc_limit(context) + + +# Size Limit Constraint + +class OBJECT_PT_bSizeLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_limit(context) + + +class BONE_PT_bSizeLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_limit(context) + + +# Rotate Like Constraint + +class OBJECT_PT_bRotateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rotate_like(context) + + +class BONE_PT_bRotateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rotate_like(context) + + +# Locate Like Constraint + +class OBJECT_PT_bLocateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_locate_like(context) + + +class BONE_PT_bLocateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_locate_like(context) + + +# Size Like Constraint + +class OBJECT_PT_bSizeLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_like(context) + + +class BONE_PT_bSizeLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_like(context) + + +# Same Volume Constraint + +class OBJECT_PT_bSameVolumeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_same_volume(context) + + +class BONE_PT_bSameVolumeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_same_volume(context) + + +# Trans Like Constraint + +class OBJECT_PT_bTransLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trans_like(context) + + +class BONE_PT_bTransLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trans_like(context) + + +# Action Constraint + +class OBJECT_PT_bActionConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_action(context) + + +class BONE_PT_bActionConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_action(context) + + +class OBJECT_PT_bActionConstraint_target(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bActionConstraint" + bl_label = "Target" + + def draw(self, context): + self.draw_action_target(context) + + +class BONE_PT_bActionConstraint_target(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bActionConstraint" + bl_label = "Target" + + def draw(self, context): + self.draw_action_target(context) + + +class OBJECT_PT_bActionConstraint_action(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bActionConstraint" + bl_label = "Action" + + def draw(self, context): + self.draw_action_action(context) + + +class BONE_PT_bActionConstraint_action(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bActionConstraint" + bl_label = "Action" + + def draw(self, context): + self.draw_action_action(context) + + +# Lock Track Constraint + +class OBJECT_PT_bLockTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_lock_track(context) + + +class BONE_PT_bLockTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_lock_track(context) + + +# Disance Limit Constraint + +class OBJECT_PT_bDistLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_dist_limit(context) + + +class BONE_PT_bDistLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_dist_limit(context) + + +# Stretch To Constraint + +class OBJECT_PT_bStretchToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_stretch_to(context) + + +class BONE_PT_bStretchToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_stretch_to(context) + + +# Min Max Constraint + +class OBJECT_PT_bMinMaxConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_min_max(context) + + +class BONE_PT_bMinMaxConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_min_max(context) + + +# Clamp To Constraint + +class OBJECT_PT_bClampToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_clamp_to(context) + + +class BONE_PT_bClampToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_clamp_to(context) + + +# Transform Constraint + +class OBJECT_PT_bTransformConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform(context) + + +class BONE_PT_bTransformConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform(context) + + +class OBJECT_PT_bTransformConstraint_source(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Source" + + def draw(self, context): + self.draw_transform_source(context) + + +class BONE_PT_bTransformConstraint_source(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Source" + + def draw(self, context): + self.draw_transform_source(context) + + +class OBJECT_PT_bTransformConstraint_mapping(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Mapping" + + def draw(self, context): + self.draw_transform_mapping(context) + + +class BONE_PT_bTransformConstraint_mapping(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Mapping" + + def draw(self, context): + self.draw_transform_mapping(context) + + +class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Destination" + + def draw(self, context): + self.draw_transform_destination(context) + + +class BONE_PT_bTransformConstraint_destination(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Destination" + + def draw(self, context): + self.draw_transform_destination(context) + + +# Shrinkwrap Constraint + +class OBJECT_PT_bShrinkwrapConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_shrinkwrap(context) + + +class BONE_PT_bShrinkwrapConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_shrinkwrap(context) + + +# Damp Track Constraint + +class OBJECT_PT_bDampTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_damp_track(context) + + +class BONE_PT_bDampTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_damp_track(context) + + +# Spline IK Constraint + +class BONE_PT_bSplineIKConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_spline_ik(context) + + +class BONE_PT_bSplineIKConstraint_fitting(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bSplineIKConstraint" + bl_label = "Fitting" + + def draw(self, context): + self.draw_spline_ik_fitting(context) + + +class BONE_PT_bSplineIKConstraint_chain_scaling(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bSplineIKConstraint" + bl_label = "Chain Scaling" + + def draw(self, context): + self.draw_spline_ik_chain_scaling(context) + + +# Pivot Constraint + +class OBJECT_PT_bPivotConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_pivot(context) + + +class BONE_PT_bPivotConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_pivot(context) + + +# Follow Track Constraint + +class OBJECT_PT_bFollowTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_track(context) + + +class BONE_PT_bFollowTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_track(context) + + +# Camera Solver Constraint + +class OBJECT_PT_bCameraSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_camera_solver(context) + + +class BONE_PT_bCameraSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_camera_solver(context) + + +# Object Solver Constraint + +class OBJECT_PT_bObjectSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_object_solver(context) + + +class BONE_PT_bObjectSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_object_solver(context) + + +# Transform Cache Constraint + +class OBJECT_PT_bTransformCacheConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform_cache(context) + + +class BONE_PT_bTransformCacheConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform_cache(context) + + +# Python Constraint + +class OBJECT_PT_bPythonConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_python_constraint(context) + +class BONE_PT_bPythonConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_python_constraint(context) + + + +# Armature Constraint + +class OBJECT_PT_bArmatureConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_armature(context) + + +class BONE_PT_bArmatureConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_armature(context) + + +class OBJECT_PT_bArmatureConstraint_bones(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bArmatureConstraint" + bl_label = "Bones" + + def draw(self, context): + self.draw_armature_bones(context) + + +class BONE_PT_bArmatureConstraint_bones(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bArmatureConstraint" + bl_label = "Bones" + + def draw(self, context): + self.draw_armature_bones(context) + + +# Inverse Kinematic Constraint + +class OBJECT_PT_bKinematicConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_kinematic(context) + + +class BONE_PT_bKinematicConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_kinematic(context) + classes = ( + # Object Panels OBJECT_PT_constraints, BONE_PT_constraints, + OBJECT_PT_bChildOfConstraint, + OBJECT_PT_bTrackToConstraint, + OBJECT_PT_bKinematicConstraint, + OBJECT_PT_bFollowPathConstraint, + OBJECT_PT_bRotLimitConstraint, + OBJECT_PT_bLocLimitConstraint, + OBJECT_PT_bSizeLimitConstraint, + OBJECT_PT_bRotateLikeConstraint, + OBJECT_PT_bLocateLikeConstraint, + OBJECT_PT_bSizeLikeConstraint, + OBJECT_PT_bSameVolumeConstraint, + OBJECT_PT_bTransLikeConstraint, + OBJECT_PT_bActionConstraint, + OBJECT_PT_bActionConstraint_target, + OBJECT_PT_bActionConstraint_action, + OBJECT_PT_bLockTrackConstraint, + OBJECT_PT_bDistLimitConstraint, + OBJECT_PT_bStretchToConstraint, + OBJECT_PT_bMinMaxConstraint, + OBJECT_PT_bClampToConstraint, + OBJECT_PT_bTransformConstraint, + OBJECT_PT_bTransformConstraint_source, + OBJECT_PT_bTransformConstraint_mapping, + OBJECT_PT_bTransformConstraint_destination, + OBJECT_PT_bShrinkwrapConstraint, + OBJECT_PT_bDampTrackConstraint, + OBJECT_PT_bPivotConstraint, + OBJECT_PT_bFollowTrackConstraint, + OBJECT_PT_bCameraSolverConstraint, + OBJECT_PT_bObjectSolverConstraint, + OBJECT_PT_bTransformCacheConstraint, + OBJECT_PT_bPythonConstraint, + OBJECT_PT_bArmatureConstraint, + OBJECT_PT_bArmatureConstraint_bones, + # Bone panels + BONE_PT_bChildOfConstraint, + BONE_PT_bTrackToConstraint, + BONE_PT_bKinematicConstraint, + BONE_PT_bFollowPathConstraint, + BONE_PT_bRotLimitConstraint, + BONE_PT_bLocLimitConstraint, + BONE_PT_bSizeLimitConstraint, + BONE_PT_bRotateLikeConstraint, + BONE_PT_bLocateLikeConstraint, + BONE_PT_bSizeLikeConstraint, + BONE_PT_bSameVolumeConstraint, + BONE_PT_bTransLikeConstraint, + BONE_PT_bActionConstraint, + BONE_PT_bActionConstraint_target, + BONE_PT_bActionConstraint_action, + BONE_PT_bLockTrackConstraint, + BONE_PT_bDistLimitConstraint, + BONE_PT_bStretchToConstraint, + BONE_PT_bMinMaxConstraint, + BONE_PT_bClampToConstraint, + BONE_PT_bTransformConstraint, + BONE_PT_bTransformConstraint_source, + BONE_PT_bTransformConstraint_mapping, + BONE_PT_bTransformConstraint_destination, + BONE_PT_bShrinkwrapConstraint, + BONE_PT_bDampTrackConstraint, + BONE_PT_bSplineIKConstraint, + BONE_PT_bSplineIKConstraint_fitting, + BONE_PT_bSplineIKConstraint_chain_scaling, + BONE_PT_bPivotConstraint, + BONE_PT_bFollowTrackConstraint, + BONE_PT_bCameraSolverConstraint, + BONE_PT_bObjectSolverConstraint, + BONE_PT_bTransformCacheConstraint, + BONE_PT_bPythonConstraint, + BONE_PT_bArmatureConstraint, + BONE_PT_bArmatureConstraint_bones, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 8fe3bd77a26..020b0f8b913 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -136,6 +136,9 @@ typedef struct bConstraintTypeInfo { const bConstraintTypeInfo *BKE_constraint_typeinfo_get(struct bConstraint *con); const bConstraintTypeInfo *BKE_constraint_typeinfo_from_type(int type); +void BKE_constraint_panel_id(int type, char *r_name); +void BKE_constraint_bone_panel_id(int type, char *r_name); + /* ---------------------------------------------------------------------------- */ /* Constraint function prototypes */ diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 050e8d434ae..f215297c8a2 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5412,9 +5412,16 @@ static bConstraint *add_new_constraint_internal(const char *name, short type) /* Set up a generic constraint data-block. */ con->type = type; - con->flag |= CONSTRAINT_EXPAND | CONSTRAINT_OVERRIDE_LIBRARY_LOCAL; + con->flag |= CONSTRAINT_OVERRIDE_LIBRARY_LOCAL; con->enforce = 1.0f; + /* Only open the main panel when constraints are created, not the subpanels. */ + con->ui_expand_flag = (1 << 0); + if (type == CONSTRAINT_TYPE_ACTION || CONSTRAINT_TYPE_SPLINEIK) { + /* Expand the two subpanels in the cases where the main panel barely has any properties. */ + con->ui_expand_flag |= (1 << 1) | (1 << 2); + } + /* Determine a basic name, and info */ if (cti) { /* initialize constraint data */ diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 8d57ef25c4d..0dd1ecd0008 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -24,6 +24,7 @@ #include "BLI_utildefines.h" #include "DNA_brush_types.h" +#include "DNA_constraint_types.h" #include "DNA_genfile.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_modifier_types.h" @@ -287,5 +288,19 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) scene->eevee.motion_blur_max = 32; } } + + /* Transition to saving expansion for all of a constraint's subpanels. */ + if (!DNA_struct_elem_find(fd->filesdna, "bConstraint", "short", "ui_expand_flag")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (bConstraint *, con, &object->constraints) { + if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) { + con->ui_expand_flag = 1; + } + else { + con->ui_expand_flag = 0; + } + } + } + } } } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 15bf26a5fa8..e591f54c750 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -1318,7 +1318,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, else { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - if ((cti && cti->get_constraint_targets) && (curcon->flag & CONSTRAINT_EXPAND)) { + if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag && (1 << 0))) { ListBase targets = {NULL, NULL}; bConstraintTarget *ct; diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 59dcc9a8ace..d89e590122c 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2004,6 +2004,8 @@ void uiTemplatePathBuilder(uiLayout *layout, struct PointerRNA *root_ptr, const char *text); void uiTemplateModifiers(uiLayout *layout, struct bContext *C); +void uiTemplateConstraints(uiLayout *layout, struct bContext *C, bool use_bone_constraints); + uiLayout *uiTemplateGpencilModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); void uiTemplateGpencilColorPreview(uiLayout *layout, struct bContext *C, @@ -2018,7 +2020,7 @@ uiLayout *uiTemplateShaderFx(uiLayout *layout, struct bContext *C, struct Pointe void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C); -uiLayout *uiTemplateConstraint(uiLayout *layout, struct PointerRNA *ptr); +void uiTemplateConstraintHeader(uiLayout *layout, struct PointerRNA *ptr); void uiTemplatePreview(uiLayout *layout, struct bContext *C, struct ID *id, diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 4fff80d5def..924b886f167 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -387,9 +387,19 @@ bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func) { - int data_len = BLI_listbase_count(data); + /* Check for NULL data. */ + int data_len = 0; + Link *data_link = NULL; + if (data == NULL) { + data_len = 0; + data_link = NULL; + } + else { + data_len = BLI_listbase_count(data); + data_link = data->first; + } + int i = 0; - Link *data_link = data->first; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->type != NULL && panel->type->flag & PNL_INSTANCED) { /* The panels were reordered by drag and drop. */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 1ce1e2950d5..189441a1f10 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1823,6 +1823,20 @@ void uiTemplatePathBuilder(uiLayout *layout, * Template for building the panel layout for the active object's modifiers. * \{ */ +/** + * Get the active object or the property region's pinned object. + */ +static Object *get_context_object(const bContext *C) +{ + SpaceProperties *sbuts = CTX_wm_space_properties(C); + if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) { + return (Object *)sbuts->pinid; + } + else { + return CTX_data_active_object(C); + } +} + static void modifier_panel_id(void *md_link, char *r_name) { ModifierData *md = (ModifierData *)md_link; @@ -1834,14 +1848,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Object *ob; - SpaceProperties *sbuts = CTX_wm_space_properties(C); - if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) { - ob = (Object *)sbuts->pinid; - } - else { - ob = CTX_data_active_object(C); - } + Object *ob = get_context_object(C); ListBase *modifiers = &ob->modifiers; bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id); @@ -1874,6 +1881,162 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Constraints Template + * + * Template for building the panel layout for the active object or bone's constraints. + * \{ */ + +/** For building the panel UI for constraints. */ +#define CONSTRAINT_TYPE_PANEL_PREFIX "OBJECT_PT_" +#define CONSTRAINT_BONE_TYPE_PANEL_PREFIX "BONE_PT_" + +/** + * Check if the panel's ID starts with 'BONE', meaning it is a bone constraint. + */ +static bool constraint_panel_is_bone(Panel *panel) +{ + return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') && + (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E'); +} + +/** + * Get the constraints for the active pose bone or the active / pinned object. + */ +static ListBase *get_constraints(const bContext *C, bool use_bone_constraints) +{ + ListBase *constraints = {NULL}; + if (use_bone_constraints) { + bPoseChannel *pose_bone = CTX_data_pointer_get(C, "pose_bone").data; + if (pose_bone != NULL) { + constraints = &pose_bone->constraints; + } + } + else { + Object *ob = get_context_object(C); + if (ob != NULL) { + constraints = &ob->constraints; + } + } + return constraints; +} + +/** + * Move a constraint to the index it's moved to after a drag and drop. + */ +static void constraint_reorder(bContext *C, Panel *panel, int new_index) +{ + bool constraint_from_bone = constraint_panel_is_bone(panel); + ListBase *lb = get_constraints(C, constraint_from_bone); + + bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("CONSTRAINT_OT_move_to_index", false); + WM_operator_properties_create_ptr(&props_ptr, ot); + RNA_string_set(&props_ptr, "constraint", con->name); + RNA_int_set(&props_ptr, "index", new_index); + /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */ + RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_properties_free(&props_ptr); +} + +/** + * Get the expand flag from the active constraint to use for the panel. + */ +static short get_constraint_expand_flag(const bContext *C, Panel *panel) +{ + bool constraint_from_bone = constraint_panel_is_bone(panel); + ListBase *lb = get_constraints(C, constraint_from_bone); + + bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); + return con->ui_expand_flag; +} + +/** + * Save the expand flag for the panel and subpanels to the constraint. + */ +static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag) +{ + bool constraint_from_bone = constraint_panel_is_bone(panel); + ListBase *lb = get_constraints(C, constraint_from_bone); + + bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); + con->ui_expand_flag = expand_flag; +} + +/** + * Function with void * argument for #uiListPanelIDFromDataFunc. + * + * \note: Constraint panel types are assumed to be named with the struct name field + * concatenated to the defined prefix. + */ +static void object_constraint_panel_id(void *md_link, char *r_name) +{ + bConstraint *con = (bConstraint *)md_link; + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type); + + strcpy(r_name, CONSTRAINT_TYPE_PANEL_PREFIX); + strcat(r_name, cti->structName); +} + +static void bone_constraint_panel_id(void *md_link, char *r_name) +{ + bConstraint *con = (bConstraint *)md_link; + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type); + + strcpy(r_name, CONSTRAINT_BONE_TYPE_PANEL_PREFIX); + strcat(r_name, cti->structName); +} + +/** + * Check if the constraint panels don't match the data and rebuild the panels if so. + */ +void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + ListBase *constraints = get_constraints(C, use_bone_constraints); + + /* Switch between the bone panel ID function and the object panel ID function. */ + uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id : + object_constraint_panel_id; + + bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func); + + if (!panels_match) { + UI_panels_free_instanced(C, region); + bConstraint *con = (constraints == NULL) ? NULL : constraints->first; + for (int i = 0; con; i++, con = con->next) { + char panel_idname[MAX_NAME]; + panel_id_func(con, panel_idname); + + Panel *new_panel = UI_panel_add_instanced(sa, region, ®ion->panels, panel_idname, i); + if (new_panel) { + /* Set the list panel functionality function pointers since we don't do it with python. */ + new_panel->type->set_list_data_expand_flag = set_constraint_expand_flag; + new_panel->type->get_list_data_expand_flag = get_constraint_expand_flag; + new_panel->type->reorder = constraint_reorder; + + UI_panel_set_expand_from_list_data(C, new_panel); + } + } + } + else { + /* The expansion might have been changed elsewhere, so we still need to set it. */ + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED)) + UI_panel_set_expand_from_list_data(C, panel); + } + } +} + +#undef CONSTRAINT_TYPE_PANEL_PREFIX +#undef CONSTRAINT_BONE_TYPE_PANEL_PREFIX + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Grease Pencil Modifier Template * \{ */ @@ -2441,7 +2604,7 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Constraint Template +/** \name Constraint Header Template * \{ */ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) @@ -2449,30 +2612,15 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) ED_object_constraint_active_set(ob_v, con_v); } -/* draw panel showing settings for a constraint */ -static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) +static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { bPoseChannel *pchan = BKE_pose_channel_active(ob); - const bConstraintTypeInfo *cti; uiBlock *block; - uiLayout *result = NULL, *col, *box, *row; + uiLayout *sub; PointerRNA ptr; - char typestr[32]; short proxy_protected, xco = 0, yco = 0; // int rb_col; // UNUSED - /* get constraint typeinfo */ - cti = BKE_constraint_typeinfo_get(con); - if (cti == NULL) { - /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */ - BLI_strncpy(typestr, - (con->type == CONSTRAINT_TYPE_NULL) ? IFACE_("Null") : IFACE_("Unknown"), - sizeof(typestr)); - } - else { - BLI_strncpy(typestr, IFACE_(cti->name), sizeof(typestr)); - } - /* determine whether constraint is proxy protected or not */ if (BKE_constraints_proxylocked_owner(ob, pchan)) { proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0; @@ -2487,36 +2635,23 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr); - col = uiLayoutColumn(layout, true); - uiLayoutSetContextPointer(col, "constraint", &ptr); - - box = uiLayoutBox(col); - row = uiLayoutRow(box, false); - block = uiLayoutGetBlock(box); - - /* Draw constraint header */ + uiLayoutSetContextPointer(layout, "constraint", &ptr); - /* open/close */ - UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE); + /* Constraint type icon. */ + sub = uiLayoutRow(layout, false); + uiLayoutSetEmboss(sub, false); + uiLayoutSetRedAlert(sub, (con->flag & CONSTRAINT_DISABLE)); + uiItemL(sub, "", RNA_struct_ui_icon(ptr.type)); - /* constraint-type icon */ - uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); UI_block_emboss_set(block, UI_EMBOSS); - if (con->flag & CONSTRAINT_DISABLE) { - uiLayoutSetRedAlert(row, true); - } - if (proxy_protected == 0) { - uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiItemR(layout, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(row, con->name, ICON_NONE); + uiItemL(layout, con->name, ICON_NONE); } - uiLayoutSetRedAlert(row, false); - /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ if (proxy_protected) { UI_block_emboss_set(block, UI_EMBOSS_NONE); @@ -2555,54 +2690,20 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) UI_block_emboss_set(block, UI_EMBOSS); } else { - short prev_proxylock, show_upbut, show_downbut; - - /* Up/Down buttons: - * Proxy-constraints are not allowed to occur after local (non-proxy) constraints - * as that poses problems when restoring them, so disable the "up" button where - * it may cause this situation. - * - * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose. - */ - if (BKE_constraints_proxylocked_owner(ob, pchan)) { - if (con->prev) { - prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1; - } - else { - prev_proxylock = 0; - } - } - else { - prev_proxylock = 0; - } - - show_upbut = ((prev_proxylock == 0) && (con->prev)); - show_downbut = (con->next) ? 1 : 0; - /* enabled */ UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemR(row, &ptr, "mute", 0, "", 0); + uiItemR(layout, &ptr, "mute", 0, "", 0); UI_block_emboss_set(block, UI_EMBOSS); - uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); - - /* up/down */ - if (show_upbut || show_downbut) { - UI_block_align_begin(block); - if (show_upbut) { - uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up"); - } - - if (show_downbut) { - uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down"); - } - UI_block_align_end(block); - } + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete"); + uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete"); UI_block_emboss_set(block, UI_EMBOSS); + + /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ + uiItemS(layout); } /* Set but-locks for protected settings (magic numbers are used here!) */ @@ -2610,23 +2711,11 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint")); } - /* Draw constraint data */ - if ((con->flag & CONSTRAINT_EXPAND) == 0) { - (yco) -= 10.5f * UI_UNIT_Y; - } - else { - box = uiLayoutBox(col); - block = uiLayoutAbsoluteBlock(box); - result = box; - } - /* clear any locks set up for proxies/lib-linking */ UI_block_lock_clear(block); - - return result; } -uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) +void uiTemplateConstraintHeader(uiLayout *layout, PointerRNA *ptr) { Object *ob; bConstraint *con; @@ -2634,7 +2723,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) /* verify we have valid data */ if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) { RNA_warning("Expected constraint on object"); - return NULL; + return; } ob = (Object *)ptr->owner_id; @@ -2642,7 +2731,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) if (!ob || !(GS(ob->id.name) == ID_OB)) { RNA_warning("Expected constraint on object"); - return NULL; + return; } UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE); @@ -2651,11 +2740,11 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) if (con->type == CONSTRAINT_TYPE_KINEMATIC) { bKinematicConstraint *data = con->data; if (data->flag & CONSTRAINT_IK_TEMP) { - return NULL; + return; } } - return draw_constraint(layout, ob, con); + draw_constraint_header(layout, ob, con); } /** \} */ diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index a2d33ffe413..5746480e3f8 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1557,6 +1557,77 @@ void CONSTRAINT_OT_move_up(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ +/** \name Move Constraint To Index Operator + * \{ */ + +static int constraint_move_to_index_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(op, ob, 0); + + int new_index = RNA_int_get(op->ptr, "index"); + if (new_index < 0) { + new_index = 0; + } + + if (con) { + ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL); + int current_index = BLI_findindex(conlist, con); + BLI_assert(current_index >= 0); + + BLI_listbase_link_move(conlist, con, new_index - current_index); + + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +static int constraint_move_to_index_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (edit_constraint_invoke_properties(C, op)) { + return constraint_move_to_index_exec(C, op); + } + else { + return OPERATOR_CANCELLED; + } +} + +void CONSTRAINT_OT_move_to_index(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Move Constraint To Index"; + ot->idname = "CONSTRAINT_OT_move_to_index"; + ot->description = + "Change the constraint's position in the list so it evaluates after the set number of " + "others"; + + /* callbacks */ + ot->exec = constraint_move_to_index_exec; + ot->invoke = constraint_move_to_index_invoke; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + RNA_def_int(ot->srna, + "index", + 0, + 0, + INT_MAX, + "Index", + "The index to move the constraint to", + 0, + INT_MAX); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ /** \name Clear Pose Constraints Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index c5a6e38fbcb..1cd91f7c1f3 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -217,6 +217,7 @@ void POSE_OT_ik_clear(struct wmOperatorType *ot); void CONSTRAINT_OT_delete(struct wmOperatorType *ot); void CONSTRAINT_OT_move_up(struct wmOperatorType *ot); +void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot); void CONSTRAINT_OT_move_down(struct wmOperatorType *ot); void CONSTRAINT_OT_stretchto_reset(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 05f1ced8615..84bc10f8064 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -179,6 +179,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(CONSTRAINT_OT_delete); WM_operatortype_append(CONSTRAINT_OT_move_up); WM_operatortype_append(CONSTRAINT_OT_move_down); + WM_operatortype_append(CONSTRAINT_OT_move_to_index); WM_operatortype_append(CONSTRAINT_OT_stretchto_reset); WM_operatortype_append(CONSTRAINT_OT_limitdistance_reset); WM_operatortype_append(CONSTRAINT_OT_childof_set_inverse); diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 65087a6d459..85d9a04a902 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -61,7 +61,8 @@ typedef struct bConstraint { /** Constraint name, MAX_NAME. */ char name[64]; - char _pad[2]; + /* Flag for panel and subpanel closed / open state in the UI. */ + short ui_expand_flag; /** Amount of influence exherted by constraint (0.0-1.0). */ float enforce; @@ -689,8 +690,8 @@ typedef enum eBConstraint_Types { /* flag 0x20 (1 << 5) was used to indicate that a constraint was evaluated * using a 'local' hack for posebones only. */ typedef enum eBConstraint_Flags { - /* expand for UI */ - CONSTRAINT_EXPAND = (1 << 0), + /* Expansion for old box constraint layouts. Just for versioning. */ + CONSTRAINT_EXPAND_DEPRECATED = (1 << 0), /* pre-check for illegal object name or bone name */ CONSTRAINT_DISABLE = (1 << 2), /* to indicate which Ipo should be shown, maybe for 3d access later too */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 5405cb4e24a..af300f6e088 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -2483,7 +2483,7 @@ static void rna_def_constraint_location_limit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM); RNA_def_property_ui_text( - prop, "For Transform", "Transforms are affected by this constraint as well"); + prop, "Affect Transform", "Transforms are affected by this constraint as well"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_define_lib_overridable(false); @@ -2556,7 +2556,7 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM); RNA_def_property_ui_text( - prop, "For Transform", "Transforms are affected by this constraint as well"); + prop, "Affect Transform", "Transforms are affected by this constraint as well"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_define_lib_overridable(false); @@ -2644,7 +2644,7 @@ static void rna_def_constraint_size_limit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM); RNA_def_property_ui_text( - prop, "For Transform", "Transforms are affected by this constraint as well"); + prop, "Affect Transform", "Transforms are affected by this constraint as well"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_define_lib_overridable(false); @@ -2684,7 +2684,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", LIMITDIST_TRANSFORM); RNA_def_property_ui_text( - prop, "For Transform", "Transforms are affected by this constraint as well"); + prop, "Affect Transform", "Transforms are affected by this constraint as well"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_define_lib_overridable(false); @@ -3380,7 +3380,7 @@ void RNA_def_constraint(BlenderRNA *brna) prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_EXPAND); + RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); RNA_def_property_ui_text(prop, "Expanded", "Constraint's panel is expanded in UI"); RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index fd65b713d15..8bfee4cc26c 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1213,6 +1213,15 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_function_ui_description(func, "Generates the UI layout for the modifier stack"); + func = RNA_def_function(srna, "template_constraints", "uiTemplateConstraints"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Generates the panels for the constraint stack"); + RNA_def_boolean(func, + "use_bone_constraints", + true, + "", + "Add panels for bone constraints instead of object constraints"); + func = RNA_def_function(srna, "template_greasepencil_modifier", "uiTemplateGpencilModifier"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_function_ui_description(func, "Generates the UI layout for grease pencil modifiers"); @@ -1251,12 +1260,10 @@ void RNA_api_ui_layout(StructRNA *srna) "", "Optionally limit the items which can be selected"); - func = RNA_def_function(srna, "template_constraint", "uiTemplateConstraint"); - RNA_def_function_ui_description(func, "Generates the UI layout for constraints"); + func = RNA_def_function(srna, "template_constraint_header", "uiTemplateConstraintHeader"); + RNA_def_function_ui_description(func, "Generates the header for constraint panels"); parm = RNA_def_pointer(func, "data", "Constraint", "", "Constraint data"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in"); - RNA_def_function_return(func, parm); func = RNA_def_function(srna, "template_preview", "uiTemplatePreview"); RNA_def_function_ui_description( |