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))
Esempio n. 2
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