Skip to main content
added 4889 characters in body
Source Link

EDIT: as pointed out in the comments, I'm inserting the code inline so it stays here once the pastebin turns to dust.

bl_info = {
        "name": "Bone Panel extension",
        "author": "Fernando D'Andrea - Rockhead Studios (thanks to Jerryno for the angle calculation code)",
        "version": (0, 1),
        "blender": (2, 78, 0),
        "location": "Properties > Bone Constraints > Inverse Kinematics",
        "description": "Extends IK panel funcionality",
        "warning": "",
        "wiki_url": "",
        "category": "Rigging"
        }
       
import bpy
import mathutils
import bmesh
 
def dandrea_IKConstraint_panel(self, context):
       
        _bone = context.pose_bone #context.object.pose.bones
        #self.layout.label(str(_bone.name))
        #self.layout.label(str(_bone.constraints))
        #self.layout.label(str(len(_bone.constraints)))
        for cons in _bone.constraints:
                if cons.type=='IK':
                        self.layout.operator("constraint.dandrea_adjust_pole", icon = 'OUTLINER_OB_ARMATURE', text='Adjust Pole Angle:    ' + _bone.name + ' ("'+ cons.name +'")').constraint = cons.name
 
 
class dandrea_adjust_pole(bpy.types.Operator):
        """Adds functionality to the IK constraint panel."""
        bl_idname = "constraint.dandrea_adjust_pole"
        bl_label = "Adjust IK Pole Angle"
        bl_icon = 'OUTLINER_OB_ARMATURE'
        constraint = bpy.props.StringProperty(name="IK Name", description="IK constraint name to set", default="")
       
        @classmethod
        def poll(cls, context):
                #implement poll
                return True
                       
        def execute(self, context):
       
                def signed_angle(vector_u, vector_v, normal):
                        # Normal specifies orientation
                        angle = vector_u.angle(vector_v)
                        if vector_u.cross(vector_v).angle(normal) < 1:
                                angle = -angle
                        return angle
 
                def get_pole_angle(base_bone, ik_bone, pole_location):
                        pole_normal = (ik_bone.tail - base_bone.head).cross(pole_location - base_bone.head)
                        projected_pole_axis = pole_normal.cross(base_bone.tail - base_bone.head)
                        return signed_angle(base_bone.x_axis, projected_pole_axis, base_bone.tail - base_bone.head)
 
                context_mode = context.mode
                bpy.ops.object.mode_set(mode='POSE')
                ik_bone = context.pose_bone
                print (ik_bone)
                cons = ik_bone.constraints[self.constraint]
                if cons is None:
                        return {'CANCELLED'}
                cons_mute = cons.mute
                base_bone = ik_bone
                for i in range(1, cons.chain_count):
                        base_bone = base_bone.parent
                if base_bone is None:
                        return {'CANCELLED'}
                pole_bone = cons.pole_target
                if pole_bone is None:
                        return {'CANCELLED'}
                if pole_bone.type == 'ARMATURE':
                        pole_bone = context.object.pose.bones[cons.pole_subtarget]
                if pole_bone is None:
                        return {'CANCELLED'}
                print(pole_bone)
                if (base_bone is not None) and (pole_bone is not None):
                        cons.mute = False
                        bpy.ops.object.mode_set(mode='EDIT')
                        pole_angle_in_radians = get_pole_angle(base_bone,ik_bone,pole_bone.matrix.translation)
                        pole_angle_in_deg = round(180*pole_angle_in_radians/3.141592, 3)
                        print(pole_angle_in_deg)                       
                        cons.mute = cons_mute
                        bpy.ops.object.mode_set(mode=context_mode)
                        cons.pole_angle = pole_angle_in_radians
 
                return {'FINISHED'}
 
def register():
        bpy.utils.register_class(dandrea_adjust_pole)
        bpy.types.BONE_PT_constraints.append(dandrea_IKConstraint_panel)
 
def unregister():
        bpy.utils.unregister_class(dandrea_adjust_pole)
        bpy.types.BONE_PT_constraints.remove(dandrea_IKConstraint_panel)
 
if __name__ == "__main__":
        unregister()
        register()

EDIT: as pointed out in the comments, I'm inserting the code inline so it stays here once the pastebin turns to dust.

bl_info = {
        "name": "Bone Panel extension",
        "author": "Fernando D'Andrea - Rockhead Studios (thanks to Jerryno for the angle calculation code)",
        "version": (0, 1),
        "blender": (2, 78, 0),
        "location": "Properties > Bone Constraints > Inverse Kinematics",
        "description": "Extends IK panel funcionality",
        "warning": "",
        "wiki_url": "",
        "category": "Rigging"
        }
       
import bpy
import mathutils
import bmesh
 
def dandrea_IKConstraint_panel(self, context):
       
        _bone = context.pose_bone #context.object.pose.bones
        #self.layout.label(str(_bone.name))
        #self.layout.label(str(_bone.constraints))
        #self.layout.label(str(len(_bone.constraints)))
        for cons in _bone.constraints:
                if cons.type=='IK':
                        self.layout.operator("constraint.dandrea_adjust_pole", icon = 'OUTLINER_OB_ARMATURE', text='Adjust Pole Angle:    ' + _bone.name + ' ("'+ cons.name +'")').constraint = cons.name
 
 
class dandrea_adjust_pole(bpy.types.Operator):
        """Adds functionality to the IK constraint panel."""
        bl_idname = "constraint.dandrea_adjust_pole"
        bl_label = "Adjust IK Pole Angle"
        bl_icon = 'OUTLINER_OB_ARMATURE'
        constraint = bpy.props.StringProperty(name="IK Name", description="IK constraint name to set", default="")
       
        @classmethod
        def poll(cls, context):
                #implement poll
                return True
                       
        def execute(self, context):
       
                def signed_angle(vector_u, vector_v, normal):
                        # Normal specifies orientation
                        angle = vector_u.angle(vector_v)
                        if vector_u.cross(vector_v).angle(normal) < 1:
                                angle = -angle
                        return angle
 
                def get_pole_angle(base_bone, ik_bone, pole_location):
                        pole_normal = (ik_bone.tail - base_bone.head).cross(pole_location - base_bone.head)
                        projected_pole_axis = pole_normal.cross(base_bone.tail - base_bone.head)
                        return signed_angle(base_bone.x_axis, projected_pole_axis, base_bone.tail - base_bone.head)
 
                context_mode = context.mode
                bpy.ops.object.mode_set(mode='POSE')
                ik_bone = context.pose_bone
                print (ik_bone)
                cons = ik_bone.constraints[self.constraint]
                if cons is None:
                        return {'CANCELLED'}
                cons_mute = cons.mute
                base_bone = ik_bone
                for i in range(1, cons.chain_count):
                        base_bone = base_bone.parent
                if base_bone is None:
                        return {'CANCELLED'}
                pole_bone = cons.pole_target
                if pole_bone is None:
                        return {'CANCELLED'}
                if pole_bone.type == 'ARMATURE':
                        pole_bone = context.object.pose.bones[cons.pole_subtarget]
                if pole_bone is None:
                        return {'CANCELLED'}
                print(pole_bone)
                if (base_bone is not None) and (pole_bone is not None):
                        cons.mute = False
                        bpy.ops.object.mode_set(mode='EDIT')
                        pole_angle_in_radians = get_pole_angle(base_bone,ik_bone,pole_bone.matrix.translation)
                        pole_angle_in_deg = round(180*pole_angle_in_radians/3.141592, 3)
                        print(pole_angle_in_deg)                       
                        cons.mute = cons_mute
                        bpy.ops.object.mode_set(mode=context_mode)
                        cons.pole_angle = pole_angle_in_radians
 
                return {'FINISHED'}
 
def register():
        bpy.utils.register_class(dandrea_adjust_pole)
        bpy.types.BONE_PT_constraints.append(dandrea_IKConstraint_panel)
 
def unregister():
        bpy.utils.unregister_class(dandrea_adjust_pole)
        bpy.types.BONE_PT_constraints.remove(dandrea_IKConstraint_panel)
 
if __name__ == "__main__":
        unregister()
        register()
Source Link

You may want to try this: http://pasteall.org/384523/python

It's an addon. You'll have to put it in your %appdata%\Blender Foundation\Blender\2.78\scripts\addons folder or the equivalent in your platform and enable it in Blender. It adds a button to the constraints panel for each IK constraint it finds. The button puts the right number in the pole angle box.

I took @jerryno's solution and coded an addon around it, so, thank him. It's not the most elegant solution around, but it did the trick for my pipeline.