Example #1
0
    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
Example #2
0
def manual(context, src, path, **kargs):
    "Insert a keyframe manually. Sub is for sub targets, like constraints"
    # kargs:
    # sub=None, index=-1, frame=None, value=None,
    # group=None, insert_key=None, action=None, context=None

    insert_key = kargs.get('insert_key', None)
    sub = kargs.get('sub', None)
    index = kargs.get('index', -1)
    frame = kargs.get('frame', None)
    value = kargs.get('value', None)
    group = kargs.get('group', None)
    action = kargs.get('action', None)
    results = kargs.get('results', list())
    options = kargs.get('options', set())
    delete = kargs.get('delete', False)

    keyframe_type = kargs.get('type',
                              context.scene.tool_settings.keyframe_type)

    if not keyframe.poll_insert(context, insert_key, src=src):
        return results

    # if frame is None:
    #     frame = context.scene.frame_current
    # if group is None:
    #     group = keyframe.group_name(src)
    # src.keyframe_insert(path, index=index, frame=frame, group=group)
    # return

    if group is None:
        group = keyframe.group_name(src)

    obj = src.id_data
    anim = obj.animation_data_create()
    if action is None:
        key_in_action = True
        action = anim.action
        if action is None:
            action = anim.action = bpy.data.actions.new("Action")
    else:
        # When using a specified action, don't insert
        # keyframes later to determine the property's value
        key_in_action = False

    if frame is None:
        frame = context.scene.frame_current_final

    strip = Get.active_strip(anim) if anim.use_tweak_mode else None

    if strip:
        if not (strip.frame_start <= frame <= strip.frame_end):
            # frame outside of the strip's bounds
            key_in_action = False

        tweak_frame = Get.frame_to_strip(context, anim, frame=frame)
    else:
        tweak_frame = frame

    if Is.posebone(src) and not path.startswith('pose.bones'):
        keypath = utils.string('pose.bones[\"', src.name, '\"]',
                               '' if path.startswith('[') else '.', path)
    elif not Is.object(src) and hasattr(src, 'path_from_id'):
        keypath = src.path_from_id(path)
    else:
        keypath = path

    # Find the value(s) to insert keyframes for
    if hasattr(sub, path):
        base = getattr(sub, path)
    elif hasattr(src, path):
        base = getattr(src, path)
    else:
        base = eval(f'{obj!r}.{keypath}')

    if value is None:
        prop = base
    else:
        if Is.iterable(base) and not Is.iterable(value):
            prop = [value for i in base]
        elif not Is.iterable(base) and Is.iterable(value):
            prop = value[(index, 0)[index == -1]]
        else:
            prop = value

    if (not Is.iterable(prop)):
        if index != -1:
            index = -1
        if (not Is.iterable(prop)):
            props = [(index, prop)]
        else:
            props = [(index, prop[index])]
    elif (index == -1):
        props = list(enumerate(prop))
    else:
        props = [(index, prop[index])]

    def save_quats():
        quats = [0, 0, 0, 0]
        for index in range(4):
            fc = Get.fcurve(action, keypath, index=index)
            if fc:
                quats[index] = len(fc.keyframe_points)
        return quats

    def restore_quats(skip):
        nonlocal quats
        for index in range(4):
            if index == skip:
                continue
            fc = Get.fcurve(action, keypath, index=index)
            if fc and len(fc.keyframe_points) > quats[index]:
                for key in fc.keyframe_points:
                    if key.co.x == tweak_frame:
                        fc.keyframe_points.remove(key)
                        break
                else:
                    # Shouldn't happen but backup in case fail to find key
                    key_args = dict(index=index, frame=frame, group=group)
                    if sub is None:
                        src.keyframe_delete(path, **key_args)
                    else:
                        sub.keyframe_delete(path, **key_args)

    if path.endswith('rotation_quaternion') and (
        (strip and strip.blend_type == 'COMBINE') or
        (not strip and anim.action_blend_type == 'COMBINE')):
        # Combine forces keyframe insertion on all 4 channels, so reset them
        quats = save_quats()
    else:
        quats = None

    # Create curve(s) (if needed) and keyframe(s)
    for (i, v) in props:
        fc = Get.fcurve(action, keypath, i)
        new_fc = not bool(fc)
        if new_fc:
            fc = New.fcurve(action, keypath, index=i, group=group)

        if fc.lock:
            # Internal ops don't allow keyframing locked channels, so :p
            results.append((fc, None))
            continue

        if delete:
            results.append((fc, src.keyframe_delete(path, frame=frame)))
            continue

        count = len(fc.keyframe_points)

        if (value is None) and key_in_action:
            key_args = dict(index=i, frame=frame, group=group, options=options)
            if sub is None:
                src.keyframe_insert(path, **key_args)
            else:
                sub.keyframe_insert(path, **key_args)
            v = fc.evaluate(tweak_frame)

        key = fc.keyframe_points.insert(tweak_frame, v, options={'FAST'})
        # options=set({'REPLACE', 'NEEDED', 'FAST'})
        # src.keyframe_insert(path, index=i, frame=frame, group=group)

        if quats:
            restore_quats(skip=i)
            quats[i] = len(fc.keyframe_points)
            # Update key count for current index, to not remove it later

        # Update keyframe to use default preferences values

        edit = utils.prefs().edit

        key.handle_left_type = key.handle_right_type = \
            edit.keyframe_new_handle_type
        if new_fc:
            # When inserting keyframes, only change their interpolation type if the fcurve is new
            key.interpolation = edit.keyframe_new_interpolation_type

        if len(fc.keyframe_points) > count:
            # New keyframe was added
            key.type = keyframe_type

        results.append((fc, key))

    if kargs.get('index', -1) == -1:
        return results
    else:
        return results[0]