Exemple #1
0
def get_sk_drivers(blender_armature):

    drivers = []

    for child in blender_armature.children:
        if not child.data:
            continue
        # child.data can be an armature - which has no shapekeys
        if not hasattr(child.data, 'shape_keys'):
            continue
        if not child.data.shape_keys:
            continue
        if not child.data.shape_keys.animation_data:
            continue
        if not child.data.shape_keys.animation_data.drivers:
            continue
        if len(child.data.shape_keys.animation_data.drivers) <= 0:
            continue

        shapekeys_idx = {}
        cpt_sk = 0
        for sk in child.data.shape_keys.key_blocks:
            if sk == sk.relative_key:
                continue
            if sk.mute is True:
                continue
            shapekeys_idx[sk.name] = cpt_sk
            cpt_sk += 1

        # Note: channels will have some None items only for SK if some SK are not animated
        idx_channel_mapping = []
        all_sorted_channels = []
        for sk_c in child.data.shape_keys.animation_data.drivers:
            # Check if driver is valid. If not, ignore this driver channel
            try:
                # Check if driver is valid.
                # Try/Except is no more a suffisant check, starting with version Blender 3.0,
                # Blender crashs when trying to resolve path on invalid driver
                if not sk_c.is_valid:
                    continue
                sk_name = child.data.shape_keys.path_resolve(
                    get_target_object_path(sk_c.data_path)).name
            except:
                continue
            # Do not take into account this driver if corresponding SK is disabled
            if child.data.shape_keys.key_blocks[sk_name].mute is True:
                continue
            idx = shapekeys_idx[sk_name]
            idx_channel_mapping.append((shapekeys_idx[sk_name], sk_c))
        existing_idx = dict(idx_channel_mapping)
        for i in range(0, cpt_sk):
            if i not in existing_idx.keys():
                all_sorted_channels.append(None)
            else:
                all_sorted_channels.append(existing_idx[i])

        if len(all_sorted_channels) > 0:
            drivers.append((child, tuple(all_sorted_channels)))

    return tuple(drivers)
Exemple #2
0
def get_sk_driver_values(blender_object, frame, fcurves):
    sk_values = []
    for f in [f for f in fcurves if f is not None]:
        sk_values.append(
            blender_object.data.shape_keys.path_resolve(
                get_target_object_path(f.data_path)).value)

    return tuple(sk_values)
def get_sk_driver_values(blender_object_uuid, frame, fcurves, export_settings):
    sk_values = []
    blender_object = export_settings['vtree'].nodes[
        blender_object_uuid].blender_object
    for f in [f for f in fcurves if f is not None]:
        sk_values.append(
            blender_object.data.shape_keys.path_resolve(
                get_target_object_path(f.data_path)).value)

    return tuple(sk_values)
def get_sk_drivers(blender_armature):

    drivers = []

    for child in blender_armature.children:
        if not child.data:
            continue
        # child.data can be an armature - which has no shapekeys
        if not hasattr(child.data, 'shape_keys'):
            continue
        if not child.data.shape_keys:
            continue
        if not child.data.shape_keys.animation_data:
            continue
        if not child.data.shape_keys.animation_data.drivers:
            continue
        if len(child.data.shape_keys.animation_data.drivers) <= 0:
            continue

        shapekeys_idx = {}
        cpt_sk = 0
        for sk in child.data.shape_keys.key_blocks:
            if sk == sk.relative_key:
                continue
            if sk.mute is True:
                continue
            shapekeys_idx[sk.name] = cpt_sk
            cpt_sk += 1

        # Note: channels will have some None items only for SK if some SK are not animated
        idx_channel_mapping = []
        all_sorted_channels = []
        for sk_c in child.data.shape_keys.animation_data.drivers:
            # Check if driver is valid. If not, ignore this driver channel
            try:
                sk_name = child.data.shape_keys.path_resolve(
                    get_target_object_path(sk_c.data_path)).name
            except:
                continue
            idx = shapekeys_idx[sk_name]
            idx_channel_mapping.append((shapekeys_idx[sk_name], sk_c))
        existing_idx = dict(idx_channel_mapping)
        for i in range(0, cpt_sk):
            if i not in existing_idx.keys():
                all_sorted_channels.append(None)
            else:
                all_sorted_channels.append(existing_idx[i])

        drivers.append((child, tuple(all_sorted_channels)))

    return tuple(drivers)
def __gather_non_keyed_values(channels: typing.Tuple[bpy.types.FCurve],
                              blender_object: bpy.types.Object,
                              blender_object_if_armature: typing.Optional[bpy.types.Object],
                              pose_bone_if_armature: typing.Optional[bpy.types.PoseBone],
                              bake_channel: typing.Union[str, None],
                              driver_obj,
                              export_settings
                              ) ->  typing.Tuple[typing.Optional[float]]:

    non_keyed_values = []

    obj = blender_object if driver_obj is None else driver_obj

    # Note: channels has some None items only for SK if some SK are not animated
    if None not in channels:
        # classic case for object TRS or bone TRS
        # Or if all morph target are animated

        if driver_obj is not None:
            # driver of SK
            return tuple([None] * len(channels))

        if bake_channel is None:
            target = channels[0].data_path.split('.')[-1]
        else:
            target = bake_channel
        if target == "value":
            # All morph targets are animated
            return tuple([None] * len(channels))

        indices = [c.array_index for c in channels]
        indices.sort()
        length = {
            "delta_location": 3,
            "delta_rotation_euler": 3,
            "location": 3,
            "rotation_axis_angle": 4,
            "rotation_euler": 3,
            "rotation_quaternion": 4,
            "scale": 3,
            "value": len(channels)
        }.get(target)

        if length is None:
            # This is not a known target
            return ()

        for i in range(0, length):
            if bake_channel is not None:
                non_keyed_values.append({
                    "delta_location" : obj.delta_location,
                    "delta_rotation_euler" : obj.delta_rotation_euler,
                    "location" : obj.location,
                    "rotation_axis_angle" : obj.rotation_axis_angle,
                    "rotation_euler" : obj.rotation_euler,
                    "rotation_quaternion" : obj.rotation_quaternion,
                    "scale" : obj.scale
                }[target][i])
            elif i in indices:
                non_keyed_values.append(None)
            else:
                if blender_object_if_armature is None:
                    non_keyed_values.append({
                        "delta_location" : obj.delta_location,
                        "delta_rotation_euler" : obj.delta_rotation_euler,
                        "location" : obj.location,
                        "rotation_axis_angle" : obj.rotation_axis_angle,
                        "rotation_euler" : obj.rotation_euler,
                        "rotation_quaternion" : obj.rotation_quaternion,
                        "scale" : obj.scale
                    }[target][i])
                else:
                     # TODO, this is not working if the action is not active (NLA case for example)
                     trans, rot, scale = pose_bone_if_armature.matrix_basis.decompose()
                     non_keyed_values.append({
                        "location": trans,
                        "rotation_axis_angle": rot,
                        "rotation_euler": rot,
                        "rotation_quaternion": rot,
                        "scale": scale
                        }[target][i])

        return tuple(non_keyed_values)

    else:
        # We are in case of morph target, where all targets are not animated
        # So channels has some None items
        first_channel = [c for c in channels if c is not None][0]
        object_path = get_target_object_path(first_channel.data_path)
        if object_path:
            shapekeys_idx = {}
            cpt_sk = 0
            for sk in obj.data.shape_keys.key_blocks:
                if sk == sk.relative_key:
                    continue
                if sk.mute is True:
                    continue
                shapekeys_idx[cpt_sk] = sk.name
                cpt_sk += 1

        for idx_c, channel in enumerate(channels):
            if channel is None:
                non_keyed_values.append(obj.data.shape_keys.key_blocks[shapekeys_idx[idx_c]].value)
            else:
                non_keyed_values.append(None)

        return tuple(non_keyed_values)
def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
                    parent_inverse,
                    blender_object_if_armature: typing.Optional[bpy.types.Object],
                    non_keyed_values: typing.Tuple[typing.Optional[float]],
                    bake_bone: typing.Union[str, None],
                    bake_channel: typing.Union[str, None],
                    bake_range_start,
                    bake_range_end,
                    action_name,
                    driver_obj,
                    export_settings
                    ) -> gltf2_io.Accessor:
    """Gather the data of the keyframes."""
    keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature,
                                                                                  channels,
                                                                                  non_keyed_values,
                                                                                  bake_bone,
                                                                                  bake_channel,
                                                                                  bake_range_start,
                                                                                  bake_range_end,
                                                                                  action_name,
                                                                                  driver_obj,
                                                                                  export_settings)
    if bake_bone is not None:
        target_datapath = "pose.bones['" + bake_bone + "']." + bake_channel
    else:
        target_datapath = [c for c in channels if c is not None][0].data_path

    is_yup = export_settings[gltf2_blender_export_keys.YUP]

    # bone animations need to be handled differently as they are in a different coordinate system
    if bake_bone is None:
        object_path = get_target_object_path(target_datapath)
    else:
        object_path = None
    is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "")

    if is_armature_animation:
        if bake_bone is None:
            bone = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature, object_path)
        else:
            bone = blender_object_if_armature.pose.bones[bake_bone]
        if isinstance(bone, bpy.types.PoseBone):
            if bone.parent is None:
                axis_basis_change = mathutils.Matrix.Identity(4)
                if export_settings[gltf2_blender_export_keys.YUP]:
                    axis_basis_change = mathutils.Matrix(
                        ((1.0, 0.0, 0.0, 0.0),
                         (0.0, 0.0, 1.0, 0.0),
                         (0.0, -1.0, 0.0, 0.0),
                         (0.0, 0.0, 0.0, 1.0)))
                correction_matrix_local = axis_basis_change @ bone.bone.matrix_local
            else:
                correction_matrix_local = (
                    bone.parent.bone.matrix_local.inverted() @
                    bone.bone.matrix_local
                )

            transform = correction_matrix_local
        else:
            transform = mathutils.Matrix.Identity(4)
    else:
        transform = parent_inverse

    values = []
    for keyframe in keyframes:
        # Transform the data and build gltf control points
        value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform)
        if is_yup and not is_armature_animation:
            value = gltf2_blender_math.swizzle_yup(value, target_datapath)
        keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)

        if keyframe.in_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            if not isinstance(value, list):
                in_tangent = value - in_tangent
            else:
                in_tangent = [value[i] - in_tangent[i] for i in range(len(value))]
            keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value  # append

        if keyframe.out_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            if not isinstance(value, list):
                out_tangent = value - out_tangent
            else:
                out_tangent = [value[i] - out_tangent[i] for i in range(len(value))]
            keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent)  # append

        values += keyframe_value

    # store the keyframe data in a binary buffer
    component_type = gltf2_io_constants.ComponentType.Float
    if get_target_property_name(target_datapath) == "value":
        # channels with 'weight' targets must have scalar accessors
        data_type = gltf2_io_constants.DataType.Scalar
    else:
        data_type = gltf2_io_constants.DataType.vec_type_from_num(len(keyframes[0].value))

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(values, component_type),
        byte_offset=None,
        component_type=component_type,
        count=len(values) // gltf2_io_constants.DataType.num_elements(data_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type
    )
Exemple #7
0
def __gather_output(channels: typing.Tuple[bpy.types.FCurve], parent_inverse,
                    blender_object_if_armature: typing.Optional[
                        bpy.types.Object],
                    export_settings) -> gltf2_io.Accessor:
    """Gather the data of the keyframes."""
    keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(
        channels, export_settings)

    target_datapath = channels[0].data_path

    transform = parent_inverse

    is_yup = export_settings[gltf2_blender_export_keys.YUP]

    object_path = get_target_object_path(target_datapath)
    is_armature_animation = blender_object_if_armature is not None and object_path != ""
    if is_armature_animation:
        bone = blender_object_if_armature.path_resolve(object_path)
        if isinstance(bone, bpy.types.PoseBone):
            if bone.parent is not None:
                parent_transform = bone.parent.bone.matrix_local
                transform = gltf2_blender_math.multiply(
                    transform, parent_transform.inverted())
                # if not is_yup:
                #     transform = gltf2_blender_math.multiply(transform, gltf2_blender_math.to_zup())
            else:
                # only apply the y-up conversion to root bones, as child bones already are in the y-up space
                if is_yup:
                    transform = gltf2_blender_math.multiply(
                        transform, gltf2_blender_math.to_yup())
            local_transform = bone.bone.matrix_local
            transform = gltf2_blender_math.multiply(transform, local_transform)

    values = []
    for keyframe in keyframes:
        # Transform the data and extract
        value = gltf2_blender_math.transform(keyframe.value, target_datapath,
                                             transform)
        if is_yup and not is_armature_animation:
            value = gltf2_blender_math.swizzle_yup(value, target_datapath)
        keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)
        if keyframe.in_tangent is not None:
            in_tangent = gltf2_blender_math.transform(keyframe.in_tangent,
                                                      target_datapath,
                                                      transform)
            if is_yup and blender_object_if_armature is None:
                in_tangent = gltf2_blender_math.swizzle_yup(
                    in_tangent, target_datapath)
            keyframe_value = gltf2_blender_math.mathutils_to_gltf(
                in_tangent) + keyframe_value
        if keyframe.out_tangent is not None:
            out_tangent = gltf2_blender_math.transform(keyframe.out_tangent,
                                                       target_datapath,
                                                       transform)
            if is_yup and blender_object_if_armature is None:
                out_tangent = gltf2_blender_math.swizzle_yup(
                    out_tangent, target_datapath)
            keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(
                out_tangent)
        values += keyframe_value

    component_type = gltf2_io_constants.ComponentType.Float
    if get_target_property_name(target_datapath) == "value":
        # channels with 'weight' targets must have scalar accessors
        data_type = gltf2_io_constants.DataType.Scalar
    else:
        data_type = gltf2_io_constants.DataType.vec_type_from_num(
            len(keyframes[0].value))

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(
            values, component_type),
        byte_offset=None,
        component_type=component_type,
        count=len(values) //
        gltf2_io_constants.DataType.num_elements(data_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type)
def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
                    parent_inverse,
                    blender_object_if_armature: typing.Optional[bpy.types.Object],
                    export_settings
                    ) -> gltf2_io.Accessor:
    """Gather the data of the keyframes."""
    keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature,
                                                                                  channels,
                                                                                  export_settings)

    target_datapath = channels[0].data_path

    transform = mathutils.Matrix.Identity(4)

    is_yup = export_settings[gltf2_blender_export_keys.YUP]

    # bone animations need to be handled differently as they are in a different coordinate system
    object_path = get_target_object_path(target_datapath)
    is_armature_animation = blender_object_if_armature is not None and object_path != ""

    if is_armature_animation:
        bone = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature, object_path)
        if isinstance(bone, bpy.types.PoseBone):
            if bone.parent is None:
                axis_basis_change = mathutils.Matrix.Identity(4)
                if export_settings[gltf2_blender_export_keys.YUP]:
                    axis_basis_change = mathutils.Matrix(
                        ((1.0, 0.0, 0.0, 0.0),
                         (0.0, 0.0, 1.0, 0.0),
                         (0.0, -1.0, 0.0, 0.0),
                         (0.0, 0.0, 0.0, 1.0)))
                correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, bone.bone.matrix_local)
            else:
                correction_matrix_local = gltf2_blender_math.multiply(
                    bone.parent.bone.matrix_local.inverted(), bone.bone.matrix_local)

            transform = correction_matrix_local

    values = []
    for keyframe in keyframes:
        # Transform the data and build gltf control points
        value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform)
        if is_yup and not is_armature_animation:
            value = gltf2_blender_math.swizzle_yup(value, target_datapath)
        keyframe_value = gltf2_blender_math.mathutils_to_gltf(value)

        if keyframe.in_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            in_tangent = value - in_tangent if not isinstance(value, list) else [value[0] - in_tangent[0]]
            keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value  # append

        if keyframe.out_tangent is not None:
            # we can directly transform the tangent as it currently is represented by a control point
            out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform)
            if is_yup and blender_object_if_armature is None:
                out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath)
            # the tangent in glTF is relative to the keyframe value
            out_tangent = value - out_tangent if not isinstance(value, list) else [value[0] - out_tangent[0]]
            keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent)  # append

        values += keyframe_value

    # store the keyframe data in a binary buffer
    component_type = gltf2_io_constants.ComponentType.Float
    if get_target_property_name(target_datapath) == "value":
        # channels with 'weight' targets must have scalar accessors
        data_type = gltf2_io_constants.DataType.Scalar
    else:
        data_type = gltf2_io_constants.DataType.vec_type_from_num(len(keyframes[0].value))

    return gltf2_io.Accessor(
        buffer_view=gltf2_io_binary_data.BinaryData.from_list(values, component_type),
        byte_offset=None,
        component_type=component_type,
        count=len(values) // gltf2_io_constants.DataType.num_elements(data_type),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=data_type
    )