''' Copyright (C) 2015 Andreas Esau andreasesau@gmail.com Created by Andreas Esau This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ''' import bpy import bpy_extras import bpy_extras.view3d_utils from math import radians import mathutils from mathutils import Vector, Matrix, Quaternion import math import bmesh from bpy.props import FloatProperty, IntProperty, BoolProperty, StringProperty, CollectionProperty, FloatVectorProperty, EnumProperty, IntVectorProperty import os from bpy_extras.io_utils import ExportHelper, ImportHelper import json from bpy.app.handlers import persistent from .. functions import * from .. functions_draw import * import traceback class BindMeshToBones(bpy.types.Operator): bl_idname = "coa_tools.bind_mesh_to_bones" bl_label = "Bind Mesh To Selected Bones" bl_description = "Bind mesh to selected bones." bl_options = {"REGISTER"} ob_name = StringProperty() armature = None sprite_object = None @classmethod def poll(cls, context): return True def execute(self, context): obj = bpy.data.objects[self.ob_name] self.sprite_object = get_sprite_object(obj) self.armature = get_armature(self.sprite_object) set_weights(self,context,obj) msg = '"'+obj.name+'"' + " has been bound to selected Bones." self.report({'INFO'},msg) return {"FINISHED"} ######################################################################################################################################### Quick Armature class QuickArmature(bpy.types.Operator): bl_idname = "scene.coa_quick_armature" bl_label = "Quick Armature" def __init__(self): self.distance = .1 self.cur_distance = 0 self.old_coord = Vector((0,0,0)) self.mouse_press = False self.mouse_press_hist = False self.inside_area = False self.show_manipulator = False self.current_bone = None self.object_hover = None self.object_hover_hist = None self.in_view_3d = False self.armature_mode = None self.set_waits = False self.mouse_click_vec = Vector((0,0,0)) self.shift = False self.shift_hist = False self.sprite_object = None self.alt = False self.alt_hist = False self.selected_objects = [] self.active_object = None self.armature = None self.emulate_3_button = False self.obj_settings = {} self.cursor_location = Vector((0,0,0)) def project_cursor(self, event): coord = mathutils.Vector((event.mouse_region_x, event.mouse_region_y)) transform = bpy_extras.view3d_utils.region_2d_to_location_3d region = bpy.context.region rv3d = bpy.context.space_data.region_3d #### cursor used for the depth location of the mouse depth_location = bpy.context.scene.cursor_location #depth_location = bpy.context.active_object.location ### creating 3d vector from the cursor end = transform(region, rv3d, coord, depth_location) #end = transform(region, rv3d, coord, bpy.context.space_data.region_3d.view_location) ### Viewport origin start = bpy_extras.view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ### Cast ray from view to mouselocation if b_version_bigger_than((2,76,0)): ray = bpy.context.scene.ray_cast(start, (start+(end-start)*2000)-start ) else: ray = bpy.context.scene.ray_cast(start, start+(end-start)*2000) ### ray_cast return values have changed after blender 2.67.0 if b_version_bigger_than((2,76,0)): ray = [ray[0],ray[4],ray[5],ray[1],ray[2]] return start, end, ray def create_armature(self,context): obj = bpy.context.active_object sprite_object = get_sprite_object(obj) armature = get_armature(sprite_object) for obj2 in context.selected_objects: obj2.select = False if armature != None: context.scene.objects.active = armature armature.select = True return armature else: amt = bpy.data.armatures.new("Armature") armature = bpy.data.objects.new("Armature",amt) armature.parent = sprite_object context.scene.objects.link(armature) context.scene.objects.active = armature armature.select = True armature.show_x_ray = True #amt.draw_type = "BBONE" return armature def create_default_bone_group(self,armature): default_bone_group = None if "default_bones" not in armature.pose.bone_groups: default_bone_group = armature.pose.bone_groups.new("default_bones") default_bone_group.color_set = "THEME08" else: default_bone_group = armature.pose.bone_groups["default_bones"] return default_bone_group def create_bones(self,context,armature): if armature != None: bpy.ops.object.mode_set(mode='EDIT') bone = armature.data.edit_bones.new("Bone") ### tag bones that will be locked bone["lock_z"] = True bone["lock_rot"] = True head_position = (self.armature.matrix_world.inverted() * self.cursor_location) head_position[1] = 0 bone.head = head_position bone.hide = True bone.bbone_x = .05 bone.bbone_z = .05 for bone2 in armature.data.edit_bones: bone2.select_head = False bone2.select_tail = False if bone2 != armature.data.edit_bones.active: bone2.select = False if armature.data.edit_bones.active != None and armature.data.edit_bones.active.select == True: active_bone = armature.data.edit_bones.active bone.parent = active_bone bone.name = active_bone.name distance = Vector(active_bone.tail.xyz - bone.head.xyz).magnitude / bpy.context.space_data.region_3d.view_distance if distance < .02: bone.use_connect = True active_bone.select_tail = True active_bone.select = False bone.select = True bone.select_head = True bone.select_tail = True armature.data.edit_bones.active = bone self.current_bone = bone self.create_default_bone_group(armature) def drag_bone(self,context, event ,bone=None): ### math.atan2(0.5, 0.5)*180/math.pi if bone != None: bone.hide = False mouse_vec_norm = (self.cursor_location - self.mouse_click_vec).normalized() mouse_vec = (self.cursor_location - self.mouse_click_vec) angle = (math.atan2(mouse_vec_norm[0], mouse_vec_norm[2])*180/math.pi) cursor_local = self.armature.matrix_world.inverted() * self.cursor_location cursor_local[1] = 0 if event.shift: if angle > -22.5 and angle < 22.5: ### up bone.tail = Vector((bone.head[0],cursor_local[1],cursor_local[2])) elif angle > 22.5 and angle < 67.5: ### up right bone.tail = (bone.head + Vector((mouse_vec[0],0,mouse_vec[0]))) elif angle > 67.5 and angle < 112.5: ### right bone.tail = Vector((cursor_local[0],cursor_local[1],bone.head[2])) elif angle > 112.5 and angle < 157.5: ### down right bone.tail = (bone.head + Vector((mouse_vec[0],0,-mouse_vec[0]))) elif angle > 157.5 or angle < -157.5: ### down bone.tail = Vector((bone.head[0],cursor_local[1],cursor_local[2])) elif angle > -157.5 and angle < -112.5: ### down left bone.tail = (bone.head + Vector((mouse_vec[0],0,mouse_vec[0]))) elif angle > -112.5 and angle < -67.5: ### left bone.tail = Vector((cursor_local[0],cursor_local[1],bone.head[2])) elif angle > -67.5 and angle < -22.5: ### left up bone.tail = (bone.head + Vector((mouse_vec[0],0,-mouse_vec[0]))) else: bone.tail = cursor_local def set_parent(self,context,obj): obj.select = True bpy.ops.object.mode_set(mode='POSE') bpy.ops.object.parent_set(type='BONE') bpy.ops.object.mode_set(mode='EDIT') obj.select = False bpy.ops.ed.undo_push(message="Sprite "+obj.name+ " set parent") def return_ray_sprites(self,context,event): coord = mathutils.Vector((event.mouse_region_x, event.mouse_region_y)) transform = bpy_extras.view3d_utils.region_2d_to_location_3d region = bpy.context.region rv3d = bpy.context.space_data.region_3d depth_location = Vector((0,50,0))#bpy.context.scene.cursor_location end = transform(region, rv3d, coord, depth_location) start = bpy_extras.view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) return ray_cast(start,end,[]) def modal(self, context, event): try: self.in_view_3d = check_region(context,event) if event.alt: bpy.context.window.cursor_set("EYEDROPPER") elif not event.alt and self.in_view_3d: bpy.context.window.cursor_set("PAINT_BRUSH") else: bpy.context.window.cursor_set("DEFAULT") scene = context.scene ob = context.active_object ### lock posebone scale z value for bone in self.armature.data.bones: if "lock_z" in bone: if bone.name in ob.pose.bones: pose_bone = ob.pose.bones[bone.name] pose_bone.lock_scale[2] = True del bone["lock_z"] if "lock_rot" in bone: if bone.name in ob.pose.bones: pose_bone = ob.pose.bones[bone.name] pose_bone.lock_rotation[0] = True pose_bone.lock_rotation[1] = True del bone["lock_rot"] if self.in_view_3d: self.mouse_press_hist = self.mouse_press mouse_button = None if context.user_preferences.inputs.select_mouse == "RIGHT": mouse_button = 'LEFTMOUSE' else: mouse_button = 'RIGHTMOUSE' ### Set Mouse click if (event.value == 'PRESS') and event.type == mouse_button and self.mouse_press == False: self.mouse_press = True #return {'RUNNING_MODAL'} elif event.value in ['RELEASE','NOTHING'] and (event.type == mouse_button): self.mouse_press = False #print(event.value,"-----------",event.type) ### Cast Ray from mousePosition and set Cursor to hitPoint rayStart,rayEnd, ray = self.project_cursor(event) if ray[0] == True and ray[1] != None: #bpy.context.scene.cursor_location = ray[3] self.cursor_location = ray[3] elif rayEnd != None: #bpy.context.scene.cursor_location = rayEnd self.cursor_location = rayEnd self.cursor_location[1] = context.active_object.location[1] #bpy.context.scene.cursor_location[1] = context.active_object.location[1] if event.value in ["RELEASE"]: if self.object_hover_hist != None: self.object_hover_hist.show_x_ray = False self.object_hover_hist.select = False self.object_hover_hist.show_name = False self.object_hover_hist = None if self.object_hover != None: self.object_hover.show_x_ray = False self.object_hover.select = False self.object_hover.show_name = False if not event.alt and not event.ctrl: self.object_hover = None ### mouse just pressed if not self.mouse_press_hist and self.mouse_press and self.in_view_3d: #print("just pressed") self.mouse_click_vec = Vector(self.cursor_location) self.create_bones(context,context.active_object) self.drag_bone(context,event,self.current_bone) if context.active_bone != None: bpy.ops.armature.calculate_roll(type='GLOBAL_POS_Y') ### mouse pressed elif self.mouse_press_hist and self.mouse_press: #print("pressed") self.drag_bone(context,event,self.current_bone) if context.active_bone != None: bpy.ops.armature.calculate_roll(type='GLOBAL_POS_Y') ### mouse release elif not self.mouse_press and self.mouse_press_hist and self.current_bone != None: bpy.ops.ed.undo_push(message="Add Bone: "+self.current_bone.name) self.current_bone.hide = False self.current_bone = None self.mouse_click_vec = Vector((1000000,1000000,1000000)) self.set_waits = True bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') self.set_waits = False elif (event.alt or "ALT" in event.type) and not event.ctrl and not event.type == "P": self.object_hover_hist = self.object_hover hover_objects = self.return_ray_sprites(context,event) distance = 1000000000 if len(hover_objects) > 0: for ray in hover_objects: sprite_center = get_bounds_and_center(ray[1])[0] if ((sprite_center) - ray[3]).length < distance: distance = (sprite_center - ray[3]).length self.object_hover = ray[1] else: self.object_hover = None show_x_ray = False if self.object_hover != self.object_hover_hist: if self.object_hover != None: self.object_hover.show_name = True self.object_hover.select = True show_x_ray = self.object_hover.show_x_ray self.object_hover.show_x_ray = True if self.object_hover_hist != None: self.object_hover_hist.show_name = False self.object_hover_hist.select = False self.object_hover_hist.show_x_ray = False ### mouse just pressed if not self.mouse_press_hist and self.mouse_press and self.in_view_3d and self.object_hover != None: selected_bones = context.selected_editable_bones if ray[0] and ray[1] != None: obj = ray[1] if self.object_hover.coa_type == "MESH": #self.set_weights(context,self.object_hover) set_weights(self,context,self.object_hover) msg = '"'+obj.name+'"' + " has been bound to selected Bones." self.report({'INFO'},msg) elif self.object_hover.coa_type == "SLOT": prev_index = int(self.object_hover.coa_slot_index) for i,slot in enumerate(self.object_hover.coa_slot): self.object_hover.coa_slot_index = i set_weights(self,context,self.object_hover) msg = '"'+self.object_hover.name+'"' + " has been bound to selected Bones." self.report({'INFO'},msg) self.object_hover.coa_slot_index = prev_index return{'RUNNING_MODAL'} ### finish mode if context.active_object == None or (context.active_object != None and context.active_object != self.armature) or (context.active_object.mode != "EDIT" and context.active_object.type == "ARMATURE" and self.set_waits == False) or not self.sprite_object.coa_edit_armature: return self.exit_edit_mode(context) except Exception as e: traceback.print_exc() self.report({"ERROR"},"An Error occured, please check the console for more information") return self.exit_edit_mode(context) return {'PASS_THROUGH'} def exit_edit_mode(self,context): ### remove draw call bpy.types.SpaceView3D.draw_handler_remove(self.draw_handler, "WINDOW") bpy.context.space_data.show_manipulator = self.show_manipulator bpy.context.window.cursor_set("CROSSHAIR") #bpy.ops.object.mode_set(mode=self.armature_mode) bpy.ops.object.mode_set(mode="POSE") for pose_bone in context.active_object.pose.bones: if "default_bones" in context.active_object.pose.bone_groups and pose_bone.bone_group == None: pose_bone.bone_group = context.active_object.pose.bone_groups["default_bones"] #lock_sprites(context,get_sprite_object(context.active_object),get_sprite_object(context.active_object).lock_sprites) self.sprite_object.coa_edit_armature = False self.sprite_object.coa_edit_mode = "OBJECT" ### restore previous selection for obj in bpy.context.scene.objects: obj.select = False for obj in self.selected_objects: obj.select = True context.scene.objects.active = self.active_object context.user_preferences.inputs.use_mouse_emulate_3_button = self.emulate_3_button ### restore object settings for obj in self.obj_settings: obj.show_x_ray = self.obj_settings[obj]["show_x_ray"] obj.show_name = self.obj_settings[obj]["show_name"] return{'FINISHED'} def execute(self, context): #bpy.ops.wm.coa_modal() ### start coa modal mode if not running self.emulate_3_button = context.user_preferences.inputs.use_mouse_emulate_3_button context.user_preferences.inputs.use_mouse_emulate_3_button = False ### store object settings for obj in context.scene.objects: self.obj_settings[obj] = {"show_x_ray":obj.show_x_ray, "show_name":obj.show_name} for obj in context.scene.objects: if obj.select: self.selected_objects.append(obj) self.active_object = context.active_object self.sprite_object = get_sprite_object(context.active_object) self.sprite_object.coa_edit_armature = True self.sprite_object.coa_edit_mode = "ARMATURE" lock_sprites(context,get_sprite_object(context.active_object),False) self.armature = self.create_armature(context) self.armature.coa_hide = False self.armature_mode = context.active_object.mode bpy.ops.object.mode_set(mode='EDIT') self.show_manipulator = bpy.context.space_data.show_manipulator bpy.context.space_data.show_manipulator = False context.window_manager.modal_handler_add(self) ### call draw code args = () self.draw_handler = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") return {'RUNNING_MODAL'} def cancel(self, context): return {'CANCELLED'} def draw_callback_px(self): draw_edit_mode(self,bpy.context,color=[0.461840, 0.852381, 1.000000, 1.000000],text="Edit Armature Mode",offset=0) ######################################################################################################################################### Set Stretch To Constraint class SetStretchBone(bpy.types.Operator): bl_idname = "bone.coa_set_stretch_bone" bl_label = "Set Stretch Bone" def execute(self,context): armature = context.active_object p_bone = armature.pose.bones[context.active_pose_bone.name] bpy.ops.object.mode_set(mode="EDIT") bone_name = "Stretch_"+p_bone.name stretch_to_bone = armature.data.edit_bones.new(bone_name) stretch_to_bone.use_deform = False if p_bone.parent != None: stretch_to_bone.parent = context.active_object.data.edit_bones[p_bone.name].parent length = Vector(p_bone.tail - p_bone.head).length stretch_to_bone.head = p_bone.tail stretch_to_bone.tail = Vector((p_bone.tail[0],0, p_bone.tail[2] + length * .5)) bpy.ops.object.mode_set(mode="POSE") stretch_to_constraint = p_bone.constraints.new("STRETCH_TO") stretch_to_constraint.target = context.active_object stretch_to_constraint.subtarget = bone_name stretch_to_constraint.keep_axis = "PLANE_Z" stretch_to_constraint.volume = "VOLUME_X" set_bone_group(self, context.active_object, context.active_object.pose.bones[bone_name],group="stretch_to",theme = "THEME07") return{'FINISHED'} ######################################################################################################################################### Set IK Constraint class RemoveIK(bpy.types.Operator): bl_idname = "coa_tools.remove_ik" bl_label = "Remove IK" bl_description = "Remove Bone IK" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def execute(self, context): obj = context.active_object pose_bone = bpy.context.active_pose_bone if obj.type == "ARMATURE": ik_const_found = False for bone in obj.pose.bones: for const in bone.constraints: if const.type == "IK" and const.subtarget == pose_bone.name: ik_const_found = True if ik_const_found == False: self.report({"WARNING"}, "No IK Constraint found to delete.") return{"CANCELLED"} for bone in obj.pose.bones: copy_loc = None copy_rot = None for const in bone.constraints: if const.type == "COPY_LOCATION" and const.subtarget == pose_bone.name: copy_loc = const if const.type == "COPY_ROTATION" and const.subtarget == pose_bone.name: copy_rot = const if const.type == "IK": if const.subtarget == pose_bone.name: bone.constraints.remove(const) if copy_loc != None and copy_rot != None: bone.constraints.remove(copy_loc) bone.constraints.remove(copy_rot) obj.data.bones[bone.name].layers[0] = True obj.data.bones[bone.name].layers[1] = False obj.data.bones.active = obj.data.bones[bone.name] obj.data.bones[bone.name].select = True bpy.ops.object.mode_set(mode="EDIT") obj.data.edit_bones.remove(obj.data.edit_bones[pose_bone.name]) bpy.ops.object.mode_set(mode="POSE") return {"FINISHED"} class SetIK(bpy.types.Operator): bl_idname = "object.coa_set_ik" bl_label = "Set IK Bone" replace_bone = BoolProperty(name="Replace IK Bone",description="Replaces active Bone as IK Bone", default=True) def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) def execute(self,context): bone = context.active_object.pose.bones[context.active_pose_bone.name] bone2 = context.selected_pose_bones[0] ik_bone = None if self.replace_bone: ik_bone = bone.parent else: ik_bone = bone next_bone = bone ik_length = 0 while next_bone != bone2 and next_bone.parent != None: ik_length += 1 next_bone = next_bone.parent if not self.replace_bone: ik_length += 1 for bone3 in context.active_object.data.bones: bone3.select = False bpy.ops.object.mode_set(mode="EDIT") ik_target_name = "IK_" + bone.name ik_target = context.active_object.data.edit_bones.new("IK_" + bone.name) if bone.parent != None: ik_target.parent = context.active_object.data.edit_bones[bone.name].parent_recursive[len(context.active_object.data.edit_bones[bone.name].parent_recursive)-1] if self.replace_bone: ik_target.head = bone.head ik_target.tail = bone.tail else: ik_target.head = bone.tail ik_target.tail = ik_target.head + Vector(((bone.tail - bone.head).length,0,0)) ik_target.roll = context.active_object.data.edit_bones[bone.name].roll bpy.ops.object.mode_set(mode="POSE") context.active_object.data.bones[ik_target_name].select = True context.active_object.data.bones.active = context.active_object.data.bones[ik_target_name] ik_bone.lock_ik_x = True ik_bone.lock_ik_y = True #ik_bone.ik_stiffness_z = .9 ik_const = ik_bone.constraints.new("IK") ik_const.target = context.active_object ik_const.subtarget = ik_target_name ik_const.chain_count = ik_length set_bone_group(self, context.active_object, context.active_object.pose.bones[ik_target_name]) if self.replace_bone: copy_loc_const = bone.constraints.new("COPY_LOCATION") copy_loc_const.target = context.active_object copy_loc_const.subtarget = ik_target_name copy_rot_const = bone.constraints.new("COPY_ROTATION") copy_rot_const.target = context.active_object copy_rot_const.subtarget = ik_target_name context.active_object.data.bones[bone.name].layers[1] = True context.active_object.data.bones[bone.name].layers[0] = False bpy.ops.ed.undo_push(message="Set Ik") return{'FINISHED'} class CreateStretchIK(bpy.types.Operator): bl_idname = "coa_tools.create_stretch_ik" bl_label = "Create Stretch Ik" bl_description = "" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def get_bones(self,context,bones): p_bone = bones[0] ### parent bone c_bone = bones[0] ### control bone ik_bone = bones[0] while p_bone.parent in bones: p_bone = b_bone.parent while len(c_bone.children) > 0 and c_bone.children[0] in bones: c_bone = c_bone.children[0] ik_bones = [] ik_bone = p_bone.children[0] while ik_bone not in ik_bones and ik_bone != c_bone: ik_bones.append(ik_bone) for child in ik_bone.children: if child.select: ik_bone = child break return [p_bone,c_bone,ik_bones] def duplicate_bones(self,context,bones): bpy.ops.armature.select_all(action='DESELECT') new_bones = [] for i,bone in enumerate(bones): new_bone = context.active_object.data.edit_bones.new(bone.name + "_CTRL") new_bone.roll = bone.roll new_bone.tail = bone.tail new_bone.head = bone.head new_bones.append(new_bone) if i == 0: new_bone.parent = bone.parent else: new_bone.use_connect = True new_bone.parent = new_bones[i-1] new_bone.select = True new_bone.select_head = True new_bone.select_tail = True return new_bones def execute(self, context): obj = context.active_object if len(context.selected_pose_bones) < 3: self.report({'WARNING'},"Select 3 bones at least.") return{"CANCELLED"} ####################### create all needed bones ####################### bpy.ops.object.mode_set(mode="EDIT") bones = context.selected_bones[:] ### get deform bones p_bone_def, c_bone_def, ik_bones_def = self.get_bones(context,bones) ### duplicate deform bones self.duplicate_bones(context,[p_bone_def]+ik_bones_def+[c_bone_def]) ### get control bones bones = context.selected_bones[:] p_bone_ctrl, c_bone_ctrl, ik_bones_ctrl = self.get_bones(context,bones) p_bone_ctrl.name = p_bone_def.name + "_CTRL" c_bone_ctrl.name = c_bone_def.name + "_CTRL" for i,ik_bone in enumerate(ik_bones_ctrl): ik_bone.name = ik_bones_def[i].name + "_CTRL" c_bone_ctrl.use_connect = False c_bone_ctrl.parent = None ### create stretch to bone joint_bones_ctrl = [] for i,ik_bone in enumerate(ik_bones_ctrl): joint_bone_ctrl = context.active_object.data.edit_bones.new(ik_bones_def[i].name+ "_JOINT") joint_bone_ctrl.head = ik_bone.head joint_bone_ctrl.tail = ik_bone.head + Vector((-1,0,0)) joint_bone_ctrl.parent = ik_bone.parent#p_bone_ctrl joint_bone_ctrl.use_connect = False joint_bone_ctrl.select = True joint_bone_ctrl.select_head = True joint_bone_ctrl.select_tail = True joint_bone_ctrl.use_inherit_scale = False joint_bone_ctrl.use_inherit_rotation = False joint_bones_ctrl.append(joint_bone_ctrl) ####################### create all needed constraints ####################### ### get names from edit bone names while in edit mode. Otherwise Editbones will not be available to gather name from p_bone_ctrl_name = str(p_bone_ctrl.name) c_bone_ctrl_name = str(c_bone_ctrl.name) ik_bone_ctrl_names = [] for ik_bone in ik_bones_ctrl: ik_bone_ctrl_names.append(str(ik_bone.name)) p_bone_def_name = str(p_bone_def.name) c_bone_def_name = str(c_bone_def.name) ik_bone_def_names = [] for ik_bone in ik_bones_def: ik_bone_def_names.append(str(ik_bone.name)) ik_bone_ctrl_names = [] for ik_bone in ik_bones_ctrl: ik_bone_ctrl_names.append(str(ik_bone.name)) joint_bone_ctrl_names = [] for join_bone in joint_bones_ctrl: joint_bone_ctrl_names.append(str(join_bone.name)) ### change mode into pose mode bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="POSE") ### get pose def bones p_bone_def = obj.pose.bones[p_bone_def_name] c_bone_def = obj.pose.bones[c_bone_def_name] ik_bones_def = [] for ik_bone_name in ik_bone_def_names: ik_bones_def.append(obj.pose.bones[ik_bone_name]) ### get pose ctrl bones p_bone_ctrl = obj.pose.bones[p_bone_ctrl_name] c_bone_ctrl = obj.pose.bones[c_bone_ctrl_name] ik_bones_ctrl = [] for ik_bone_name in ik_bone_ctrl_names: ik_bones_ctrl.append(obj.pose.bones[ik_bone_name]) joint_bones_ctrl = [] for joint_bone_name in joint_bone_ctrl_names: joint_bones_ctrl.append(obj.pose.bones[joint_bone_name]) ### disable deforming for all ctrl bones ctrl_bones = [p_bone_ctrl, c_bone_ctrl] + ik_bones_ctrl + joint_bones_ctrl for bone in ctrl_bones: bone = obj.data.bones[bone.name] bone.use_deform = False c = p_bone_def.constraints.new("LIMIT_ROTATION") c.owner_space = "POSE" c.use_limit_z = True c = p_bone_def.constraints.new("STRETCH_TO") c.target = obj c.subtarget = joint_bones_ctrl[0].name c.keep_axis = "PLANE_Z" const_p_bone = c c = p_bone_def.constraints.new("LIMIT_SCALE") c.owner_space = "POSE" c.use_min_z = True c.use_max_z = True c.max_z = 1.0 c.min_z = 1.0 const_ik_bones = [] for i,ik_bone_def in enumerate(ik_bones_def): c = ik_bone_def.constraints.new("LIMIT_ROTATION") c.owner_space = "POSE" c.use_limit_z = True c = ik_bone_def.constraints.new("STRETCH_TO") c.target = obj c.keep_axis = "PLANE_Z" const_ik_bones.append(c) if i == len(ik_bones_def)-1: c.subtarget = c_bone_ctrl.name else: c.subtarget = joint_bones_ctrl[i+1].name c = ik_bone_def.constraints.new("LIMIT_SCALE") c.owner_space = "POSE" c.use_min_z = True c.use_max_z = True c.max_z = 1.0 c.min_z = 1.0 c = c_bone_def.constraints.new("COPY_TRANSFORMS") c.target = obj c.subtarget = c_bone_ctrl.name const_c_bone = c c = ik_bones_ctrl[len(ik_bones_ctrl)-1].constraints.new("IK") c.target = obj c.subtarget = c_bone_ctrl.name c.chain_count = len(ik_bones_ctrl) + 1 const_ik = c for ik_bone_ctrl in ik_bones_ctrl: ik_bone_ctrl.lock_ik_x = True ik_bone_ctrl.lock_ik_y = True ik_bone_ctrl.ik_stretch = .2 p_bone_ctrl.ik_stretch = .2 ### move bones to other layers hide_bone_names = [p_bone_ctrl_name, p_bone_def_name, c_bone_def_name] + ik_bone_ctrl_names + ik_bone_def_names for bone_name in hide_bone_names: obj.data.bones[bone_name].layers[1] = True obj.data.bones[bone_name].layers[0] = False ### store bones and constraints in Stretch IK Pointer Property p_bone_def["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"p_bone_def"]) c_bone_def["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"c_bone_def"]) for ik_bone_def in ik_bones_def: ik_bone_def["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"ik_bone_def"]) p_bone_ctrl["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"p_bone_ctrl"]) c_bone_ctrl["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"c_bone_ctrl"]) for ik_bone_ctrl in ik_bones_ctrl: ik_bone_ctrl["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"ik_bone_ctrl"]) for joint_bone_ctrl in joint_bones_ctrl: joint_bone_ctrl["coa_stretch_ik_data"] = str([c_bone_ctrl.name,"joint_bone_ctrl"]) ### set bone colors set_bone_group(self, obj, c_bone_ctrl,group = "ik_group" ,theme = "THEME09") for joint_bone_ctrl in joint_bones_ctrl: set_bone_group(self, obj, joint_bone_ctrl,group = "ik_group" ,theme = "THEME09") return {"FINISHED"} class RemoveStretchIK(bpy.types.Operator): bl_idname = "coa_tools.remove_stretch_ik" bl_label = "Remove Stretch Ik" bl_description = "" bl_options = {"REGISTER"} stretch_ik_name = StringProperty() @classmethod def poll(cls, context): return True def execute(self, context): obj = context.active_object bpy.ops.object.mode_set(mode="EDIT") for bone in obj.pose.bones: e_bone = obj.data.edit_bones[bone.name] if "coa_stretch_ik_data" in bone: name = eval(bone["coa_stretch_ik_data"])[0] type = eval(bone["coa_stretch_ik_data"])[1] if name == self.stretch_ik_name: if "_ctrl" in type: obj.data.edit_bones.remove(e_bone) elif "_def" in type: for const in bone.constraints: bone.constraints.remove(const) bpy.ops.object.mode_set(mode="POSE") del(bone["coa_stretch_ik_data"]) bone = obj.data.bones[bone.name] bone.select = True obj.data.bones.active = bone bone.layers[0] = True bone.layers[1] = False bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="POSE") return {"FINISHED"}