def bone(context, armature, name="", edit=None, pose=None, overwrite=False): "Insert a bone into an armature object" if getattr(armature, 'type', None) != 'ARMATURE': return # active = Get.active(context) # Set.active(context, armature) # Set.select(armature, True) # Set.visible(context, armature, True) # mode = armature.mode.replace('EDIT_ARMATURE', 'EDIT') Set.in_scene(context, armature) is_visible = Is.visible(context, armature) Set.visible(context, armature, True) mode = armature.mode # mode = context.mode.replace('EDIT_ARMATURE', 'EDIT') # Go into Edit mode and create a new bone ebones = armature.data.edit_bones if armature.mode != 'EDIT': Set.mode(context, 'EDIT', armature) mirror = armature.data.use_mirror_x armature.data.use_mirror_x = False children = list() if overwrite and name in ebones: for child in ebones[name].children: children.append((child, child.use_connect)) child.use_connect = False ebones.remove(ebones[name]) bone = ebones.new(name) for child, child_connect in children: child.parent = bone child.use_connect = child_connect bone.tail = ((0, 0, 1)) # If the bone's head AND tail stay at 0, # it gets deleted when leaving edit mode name = bone.name if edit: edit(bone) armature.data.use_mirror_x = mirror pbones = armature.pose.bones if pose: Set.mode(context, 'POSE', armature) bone = pbones[name] pose(bone) # Revert mode change if mode != armature.mode: # Set.active(active) Set.mode(context, mode, armature) Set.visible(context, armature, is_visible) if armature.mode == 'EDIT': bone = ebones[name] else: bone = pbones[name] return bone
def execute(self, context): if context.visible_pose_bones: is_pose = True visible_bones = [b.bone for b in context.visible_pose_bones] else: is_pose = False visible_bones = list(context.visible_bones) armatures = list() bones = list() for bone in visible_bones: if not self.extend: bone.select = bone.select_head = bone.select_tail = False arm = bone.id_data if arm in armatures: continue armatures.append(arm) if is_pose: arm.bones.active = None else: arm.edit_bones.active = None for arm in armatures: if is_pose: bones = arm.bones else: bones = arm.edit_bones for bone in bones: if bone.hide_select and Is.visible(context, bone): bone.select = True bones.active = bone return {'FINISHED'}
def selected_edit_bones(context, src=None): "Always return edit bones as list, never as None" if src: bones = src.data.edit_bones selected = [ b for b in bones if Is.selected(b) and Is.visible(context, b) ] else: selected = Get.as_list(context, 'selected_editable_bones') return selected
def selected_pose_bones(context, src=None, force: "not needed, todelete" = False): "Always return pose bones as list, never as None" if src: selected = [ b for b in src.pose.bones if Is.selected(b) and Is.visible(context, b) ] # elif not force and context.mode not in ('POSE', 'PAINT_WEIGHT'): # selected = [] else: selected = Get.as_list(context, 'selected_pose_bones') return selected
def get_rig_meshes(self, context): """ Find the selected rigs and meshes, attached together. If only a rig is selected, find all the meshes that use it. """ active = context.object if context.mode == 'PAINT_WEIGHT': meshes = {active} rig = None for mod in active.modifiers: if (mod.type == 'ARMATURE') and (mod.object in Get.objects(context)[:]): rig = mod.object if rig.mode == 'POSE': break if not rig: self.report({'ERROR'}, "Can't find rig using the active mesh") else: rig = active meshes = set() for ob in context.selected_objects: if Is.mesh(ob): for mod in ob.modifiers: if (mod.type == 'ARMATURE') and (mod.object == rig): meshes.add(ob) if not meshes: for ob in Get.objects(context): if not Is.visible(context, ob): continue for mod in ob.modifiers: if (mod.type == 'ARMATURE') and (mod.object == rig): meshes.add(ob) if not meshes: self.report({'ERROR'}, "Can't find mesh using the active rig") return (rig, meshes)
def add_obj(obj): if show_hidden is False and not Is.visible(context, obj): return None if obj not in selected: selected.append(obj)
def get_selected_keys_and_extents(): context = bpy.context pbones = context.selected_pose_bones if pbones is None: pbones = [] curve_datas = [] selected = [] objects = [] bones = [] fcurves = [] try: only_selected = context.space_data.dopesheet.show_only_selected show_hidden = context.space_data.dopesheet.show_hidden except: only_selected = True show_hidden = False def add_obj(obj): if show_hidden is False and not Is.visible(context, obj): return None if obj not in selected: selected.append(obj) def add_bone(b): if only_selected and not b.bone.select: return None add_obj(b.id_data) bones.append(b) for obj in Get.objects(context): if show_hidden is False and not Is.visible(context, obj): continue # Add object and bones if not (only_selected and not Is.selected(obj)): add_obj(obj) if obj.pose is not None: for (name, pbone) in obj.pose.bones.items(): if any((only_selected is False, Is.selected(obj), pbone in pbones,)): add_bone(pbone) # Add fcurves from objects for obj in selected: anim = obj.animation_data if anim and anim.action: fcurves.extend([(obj, fc) for fc in anim.action.fcurves]) # Scan fcurves for keyframes for obj, curve in fcurves: if curve.hide or curve.lock or not curve.select: continue first_co = None points = None last_co = None path = curve.data_path # Read path to get target's name if (path.startswith('pose.bones')): # btype = 'BONE' # bpath = path.split('"]', 1)[1] ## Transforms and custom prop # if (bpath.startswith('.')): ## constraints? # bpath = bpath.split('.', 1)[1] bname = (path.split('["', 1)[1].split('"]', 1)[0]) bone = obj.pose.bones.get(bname) elif (path.startswith('bones')): # data.bones # btype = 'BONE' # bpath = path.split('"].', 1)[1] bname = (path.split('["', 1)[1].split('"]', 1)[0]) bone = obj.bones.get(bname) else: # btype = 'OBJECT' # bpath = path bname = obj.name bone = obj if (bone is None and curve.is_valid is True) or (bone is not None and bone != obj and bone not in bones): # Bone not selected continue keyframes_referenced = [] keyframes_data = [] for keyframe in curve.keyframe_points: if keyframe.select_control_point: if first_co is None: first_co = keyframe.co else: last_co = keyframe.co keyframes_referenced.append(keyframe) keyframes_data.append({ 'co': deepcopy(keyframe.co), 'handle_left': deepcopy(keyframe.handle_left), 'handle_right': deepcopy(keyframe.handle_right) }) # needs to be all three data points! if last_co is not None: curve_datas.append([keyframes_referenced, first_co, last_co, keyframes_data, curve]) return curve_datas
def scan_actions(context, sub, selected): scn = context.scene fc = (int(scn.frame_current_final), scn.frame_current_final)[sub] frames = list() for obj in selected: if Is.gpencil(obj): for layer in obj.data.layers: for frame in layer.frames: frames.append(frame.frame_number) for obj in Get.objects_nla(context): # Poll object if pose(context): if not pose(obj): continue elif selected: bones = [ f'pose.bones[\"{b.name}\"]' for b in Get.selected_pose_bones(context, src=obj) ] if not bones: continue else: bones = list() else: if (selected and obj not in selected): continue else: bones = list() for anim in Get.animation_datas(obj): if (not anim.action): continue if anim.use_tweak_mode: for s in Get.strips(anim.id_data): if s.action == anim.action: strip = s if s.active: break # Get loop ends cycle = list() afe = strip.action_frame_end fel = fer = Get.frame_from_strip(context, strip, afe) while fel < strip.frame_end: offset = (fer - strip.frame_start) cycle.append(offset) fel += abs(offset) # If offset becomes negative, it "should" create an infinite loop # abs() forces positive, which "should" prevent loop else: strip = None for fcurve in anim.action.fcurves: path = fcurve.data_path if bones: # Bones are selected, so verify this fcurve if for one of them for bpath in bones: if path.startswith(bpath): break else: continue elif path.startswith('pose.bones[\"'): try: eval(repr(obj) + '.' + path) # Validate path bpath = path.split('\"]', 1)[0] + '\"]' bone = eval(repr(obj) + '.' + bpath) if not Is.visible(context, bone): continue except: # curve points to a removed bone or something continue scan_fcurve(context, sub, fcurve, frames, strip=strip) return sorted(set(frames))
def get_keys(self, context): scn_frame = context.scene.frame_current_final fcurves = dict() quats = dict() updates = dict() break_sync = False if hasattr(context.scene, 'sync'): break_sync = context.scene.sync.sync_3d and context.scene.sync.sync_between_3d if context.mode == 'POSE': # objects = [o for o in Get.objects(context) if o.mode == 'POSE'] objects = list() for ob in Get.objects(context): if ob.mode != 'POSE': continue bones = tuple([f"pose.bones[\"{b.name}\"]" for b in Get.selected_pose_bones(context, ob)]) if bones: objects.append((ob, bones)) if not objects: for ob in Get.objects(context): if ob.mode != 'POSE': continue bones = tuple([f"pose.bones[\"{b.name}\"]" for b in ob.pose.bones if Is.visible(context, b)]) if bones: objects.append((ob, bones)) else: objects = [(o, list()) for o in Get.selected_objects(context)] for (ob, bones) in objects: for anim in Get.animation_datas(ob): action = anim.action if not action: continue if anim.use_tweak_mode: frame = Get.frame_to_strip(context, anim, scn_frame) for s in Get.strips(anim.id_data): if s.action == action: strip = s if s.active: break blend = strip.blend_type else: frame = scn_frame blend = anim.action_blend_type offset = abs(frame - scn_frame) if (scn_frame < frame): offset *= -1 for fc in anim.action.fcurves: path = fc.data_path index = fc.array_index if len(fc.keyframe_points) < 2: continue if bones: src = None for bone in bones: if path.startswith(bone): try: eval(repr(ob) + '.' + path) # Validate path src = eval(repr(ob) + '.' + bone) attr = path.replace(bone, '', 1) if attr.startswith('.'): attr = attr.replace('.', '', 1) is_custom = False else: is_custom = True except: # curve points to a removed bone or something src = None break else: # Pose mode but bone not selected continue if src is None: # Missing bone continue else: attr = path src = ob if attr in transforms: is_custom = False elif attr.startswith('["'): is_custom = True else: # if attr.startswith(('pose.bones', 'bones')): continue # Find the property to be able to manipulate, and its current value if is_custom: prop = src split = attr.rsplit('"]["', 1) if len(split) == 2: prop = eval(repr(src) + split[0] + '"]') attr = '["' + split[1] prop_value = getattr(prop, attr) elif hasattr(src, attr): prop = getattr(src, attr) if Is.iterable(prop): # elif attr in transforms: # prop = src.path_resolve(attr) prop_value = prop[index] else: prop = src prop_value = getattr(prop, attr) else: # maybe a constraint: # pose.bones[bone.name].constraints[con.name].influence continue # Function to apply values to the bone/object, later if Is.iterable(prop): def apply(self, val): "Function to apply values to (array) in bone/object, later" self.prop[self.index] = val prop = src.path_resolve(attr) prop_value = prop[index] is_array = True else: def apply(self, val): setattr(self.prop, self.attr, val) is_array = False cache = dict( attr=attr, apply=apply, index=index, is_array=is_array, key=None, quat=None, prop=prop, src=src, value=fc.evaluate(frame), ) left = type('', (), cache) current = type('', (), cache) current.found = False right = type('', (), cache) pre_key = None # Find the current keyframe, and keys left and right if break_sync: # types = context.scene.keyframe_navigation_types types = ('KEYFRAME', 'MOVING_HOLD') for key in fc.keyframe_points: if key.co.x < frame: if (left.key is None) or (key.type in types): left.key = key left.value = key.co.y right.key = key right.value = key.co.y elif key.co.x == frame: if left.key is None: left.key = key left.value = key.co.y current.key = key current.found = True right.key = key right.value = key.co.y elif key.co.x > frame: if left.key is None: left.key = key left.value = key.co.y if (key.type in types) or key == fc.keyframe_points[-1]: right.key = key right.value = key.co.y break if not (left.key and right.key): for key in fc.keyframe_points: if key.co.x < frame: left.key = key left.value = key.co.y right.key = key right.value = key.co.y elif key.co.x == frame: if left.key is None: left.key = key left.value = key.co.y current.key = key current.found = True right.key = key right.value = key.co.y elif key.co.x > frame: if left.key is None: left.key = key left.value = key.co.y right.key = key right.value = key.co.y break if not (left.key and right.key): # Nothing to tween continue # Get info for current keyframe's defaults sVal = left.key.co.x + offset eVal = right.key.co.x + offset current.w1 = frame - sVal current.w2 = eVal - frame left.frame = left.key.co.x current.frame = frame right.frame = right.key.co.x current.in_range = False if frame < left.frame: left.value = prop_value elif right.frame < frame: right.value = prop_value else: current.in_range = True if blend == 'REPLACE': current.value = prop_value else: if not self.has_additive: self.has_additive = True if current.key: value = current.key.co.y else: value = fc.evaluate(frame) left.value = prop_value + (left.value - value) current.value = prop_value # + (value - value) right.value = prop_value + (right.value - value) if tween.update: if sVal not in updates: updates[sVal] = list() if eVal not in updates: updates[eVal] = list() updates[sVal].append(left) updates[eVal].append(right) # Add classes to memory fcurves[fc] = [left, current, right] if attr == 'rotation_quaternion': # Do math for quaternions if (action, path) not in quats: quats[(action, path)] = dict() if (src.lock_rotations_4d or not src.lock_rotation_w) \ and True not in src.lock_rotation[:]: quats[(action, path)][index] = (left, current, right) if updates: for frame in updates: context.scene.frame_set(frame) for (cls) in updates[frame]: if Is.iterable(prop): cls.value = cls.prop[cls.index] else: cls.value = getattr(cls.prop, cls.attr) context.scene.frame_set(scn_frame) for (action, path) in quats: if len(quats[action, path]) < 4: continue (w_left, w_current, w_right) = quats[action, path][0] (x_left, x_current, x_right) = quats[action, path][1] (y_left, y_current, y_right) = quats[action, path][2] (z_left, z_current, z_right) = quats[action, path][3] left_quat = [x.value for x in (w_left, x_left, y_left, z_left)] current_quat = [x.value for x in (w_current, x_current, y_current, z_current)] right_quat = [x.value for x in (w_right, x_right, y_right, z_right)] cpp.normalize_qt(left_quat) cpp.normalize_qt(current_quat) cpp.normalize_qt(right_quat) for x in (w_left, x_left, y_left, z_left): x.quat = left_quat for x in (w_current, x_current, y_current, z_current): x.quat = current_quat for x in (w_right, x_right, y_right, z_right): x.quat = right_quat return fcurves
def visible(context, object, value=True, **kargs): """ Set an object's (or bone's object's) visibility to the specified value """ scn = context.scene if not Is.object(object): if isinstance(object, bpy.types.Collection): found = False def loop(root, tree=list()): nonlocal found if root.collection == object: return True for child in root.children: if loop(child, tree): found = True tree.append(child) break if found: return tree view_layer = kargs.get('view_layer', False) if not view_layer: object.hide_viewport = not value if value or view_layer: # Only enables the collection for the view layer once tree = loop(context.view_layer.layer_collection) for col in tree: if (col.exclude == value) and (col.name == object.name): # When a collection is enabled in the view layer, # all of its child collections are as well. col.exclude = not value if value and col.collection.hide_viewport: col.collection.hide_viewport = False elif Is.posebone(object): return Set.visible(context, object.id_data, value) elif Is.bone(object) or Is.editbone(object): return Set.visible(context, Get.rig(context, object), value) else: assert None, ( "Set.visible() does not work with the specified item", object, ) return Set.in_scene(context, object) is_visible = Is.visible(context, object) object_visible = not object.hide_viewport # if Is.visible(context, object) is value: # return visible while (Is.visible(context, object) is not value): "If object isn't in the desired visiblity, loop until it is" if (object.hide_viewport is value): object.hide_viewport = not value continue is_visible = object_visible view = None for collection in object.users_collection: view = context.view_layer.layer_collection.children.get( collection.name) if not view: # collection isn't in scene or whatever continue if view.hide_viewport is value: view.hide_viewport = not value break if view is None: assert None, ( "Set.visible(): Object[", object, "] \nis hidden from viewport and I don't know how to change it" ) # collection.hide_viewport = value break return is_visible
def mode(context, mode, target=None, keep_visiblity=True): """ Set the context.mode for an object (or bone's rig) """ if not target: bpy.ops.object.mode_set(mode=mode) return context.mode == mode target = target.id_data # I can't think of a situation where I care to use # a bone/etc instead of the object # objects = context.selected_objects # for obj in objects: # select(obj, False) if Is.linked(target) and mode not in ('OBJECT', 'POSE'): return False class active_item: mode = target.mode is_visible = Set.visible(context, target, True) if mode != target.mode: modes = dict() selected = list() objects = list() # Find the visible objects of the same type as the target object # Remember their modes and selection, then deselect them for obj in Get.in_view(context): if obj == target or obj.type != target.type or not Is.visible( context, obj): continue if obj.mode not in modes: modes[obj.mode] = list() modes[obj.mode].append(obj) if Is.selected(obj): selected.append(obj) Set.select(obj, False) objects.append(obj) # Remember the target's selected status pselect = Is.selected(target) # Set the mode for the target object previous = Set.active(context, target) Set.select(target, False) bpy.ops.object.mode_set(mode=mode) # Since the operator switches all objects to the specified mode # Go through the objects and manually set them back their previous mode for pmode in modes: for obj in modes[pmode]: Set.select(obj, True) Set.active(context, obj) bpy.ops.object.mode_set(mode=pmode) Set.active(context, None) for obj in modes[pmode]: Set.select(obj, False) # Re-select previously selected objects for obj in selected: Set.select(obj, True) # reselect target if it was selected Set.select(target, pselect) # Set the active object back to the original if previous is not None: Set.active(context, previous) else: Set.active(context, target) if (keep_visiblity): Set.visible(context, target, active_item.is_visible) # for obj in objects: # select(obj, True) return (target.mode == mode)
def execute(self, context): if (self.type == 'IN_OBJECT'): rigs = set() for b in context.selected_pose_bones: rigs.add(b.id_data) for rig in rigs: for b in rig.data.bones: if Is.visible(context, b) and (not b.hide_select): Set.select(b, True) elif not self.extend: Set.select(b, False) return {'FINISHED'} elif (self.type == 'PSEUDO_LAYERS'): rigs = dict() for b in context.selected_pose_bones: rig = b.id_data if rig not in rigs: rigs[rig] = list() try: selected_layers = eval(b.layers_extra) rigs[rig].extend(selected_layers) rigs[rig] = list(set(rigs[rig])) except: pass for (rig, selected_layers) in rigs.items(): visible_layers = [ i for i, l in enumerate(rig.data.layers_extra.layers) if l.visible ] for b in rig.pose.bones: try: bone_layers = eval(b.layers_extra) except: continue if Is.visible(context, b): for b_layer in bone_layers: if (b_layer in selected_layers) and ( b_layer in visible_layers): Set.select(b, True) break else: if not self.extend: Set.select(b, False) elif not self.extend: Set.select(b, False) return {'FINISHED'} if self.type == 'GROUP': active_group = set() for b in context.selected_pose_bones: rig = b.id_data if (rig not in active_group) and b.bone_group: rig.pose.bone_groups.active = b.bone_group active_group.add(rig) try: return bpy.ops.pose.select_grouped(type=self.type, extend=self.extend) except RuntimeError as ex: self.report({'ERROR'}, str(ex).split('Error: ', 1)[-1]) return {'CANCELLED'}
def convert(self, context, obj): scn = context.scene Set.active_select(context, obj, isolate=True) coll_instanced = False # Whether or not to instance collection in full macro head = self.head group = obj.name + '-Mannequin' coll = bpy.data.collections.get(group) if coll: # Regenerating mannequin coll_instanced = True for ob in coll.objects.values(): if Is.mesh(ob) and ob.DazMannequin: bpy.data.objects.remove(ob) else: coll = bpy.data.collections.new(group) scn.collection.children.link(coll) # "temporarily" unhide collection if hidden in_view = Is.in_view(context, coll) if not in_view: Set.visible(context, coll, view_layer=True) visible = Is.visible(context, coll) if not visible: Set.visible(context, coll) # Add mannequin objects for current mesh self.generate(context, obj, obj.parent, coll) if self.macro: has_mann = False for ob in coll.objects.values(): if Is.mesh(ob): Set.select(ob) has_mann = True if has_mann: bpy.ops.object.data_transfer_mannequin_preset() # if obj.data.materials: # bpy.ops.object.data_transfer_materials() # Hide the collection and create an instancer of it # if coll: # # Set.visible(context, obj, value=False) # Set.visible(context, coll, value=False, view_layer=True) # if not coll_instanced: # inst = New.object(context, name=coll.name) # inst.instance_type = 'COLLECTION' # inst.instance_collection = coll # Set.empty_size(inst, 0) # return inst for ob in coll.objects.values(): Set.select(ob, value=False) if not visible: Set.visible(context, coll, False) if not in_view: Set.visible(context, coll, False, view_layer=True) return obj
def update_pose(self, context): # for region in context.area.regions: # if region.type == 'WINDOW': # break # else: # return self.cancel(context) region = context.region rv3d = context.space_data.region_3d gp = context.annotation_data stroke = gp.layers.active.frames[0].strokes[0] for chain in Get.sorted_chains(context.selected_pose_bones): bone_chain = list() for bone in reversed(chain): bone_chain.insert(0, bone) if bone == chain[0]: # Do location pass # continue # or break; should do the same else: pass while bone.parent not in chain: # Do unselected in betweens bone = bone.parent if not Is.visible(context, bone): # Don't rotate hidden bones continue bone_chain.insert(0, bone) bcount = len(bone_chain) - 1 gcount = len(stroke.points) - 1 # if bcount: # while gcount > bcount * 3: # # Split point count in half # index = 0 # while index < len(stroke.points) - 1: # stroke.points.pop(index=index + 1) # index += 1 # print(bcount, gcount, '\t', index, len(stroke.points)) # gcount = len(stroke.points) - 1 bone_mats = list() con_tmp = list() index = 0 for bone in bone_chain: if index > bcount: index = bcount point_index = utils.scale_range(index, 0, bcount, 0, gcount) point = stroke.points[int(point_index)] if index == 0: if not (bone.parent): bone = bone_chain[0] point = stroke.points[0] to_2d = location_3d_to_region_2d( region, rv3d, point.co) # get 2d space of stroke if to_2d: to_3d = region_2d_to_location_3d( region, rv3d, to_2d, bone.head) # keep depth of bone empty = New.object(context, bone.name) empty.empty_display_size = 0.25 empty.location = to_3d con = bone.constraints.new('COPY_LOCATION') con.target = empty con_tmp.append((bone, con, empty)) if bcount == 0: point = stroke.points[-1] else: # index += 1 point_index = utils.scale_range( 0.5, 0, bcount, 0, gcount) point = stroke.points[int(point_index)] to_2d = location_3d_to_region_2d( region, rv3d, point.co) # get 2d space of stroke if to_2d: to_3d = region_2d_to_location_3d( region, rv3d, to_2d, bone.tail) # keep depth of bone empty = New.object(context, bone.name) empty.empty_display_size = 0.1 empty.location = to_3d con = bone.constraints.new('DAMPED_TRACK') con.target = empty con_tmp.append((bone, con, empty)) index += 1 utils.update(context) for (bone, con, empty) in reversed(con_tmp): mat = Get.matrix_constraints(context, bone) # mat = Get.matrix(bone) bone_mats.append((bone, mat)) bone.constraints.remove(con) Get.objects(context, link=True).unlink(empty) for (bone, mat) in bone_mats: Set.matrix(bone, mat) keyframe.keyingset(context, selected=[bone], skip_bones=True) self.remove_annotation(context) return {'FINISHED'}