Ejemplo n.º 1
0
def gather_joint(blender_bone, export_settings):
    """
    Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes
    :param blender_bone: a blender PoseBone
    :param export_settings: the settings for this export
    :return: a glTF2 node (acting as a joint)
    """

    axis_basis_change = mathutils.Matrix.Identity(4)
    if export_settings['gltf_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)))

    # extract bone transform
    if blender_bone.parent is None:
        correction_matrix_local = gltf2_blender_math.multiply(
            axis_basis_change, blender_bone.bone.matrix_local)
    else:
        correction_matrix_local = gltf2_blender_math.multiply(
            blender_bone.parent.bone.matrix_local.inverted(),
            blender_bone.bone.matrix_local)
    matrix_basis = blender_bone.matrix_basis
    if export_settings['gltf_bake_skins']:
        gltf2_io_debug.print_console("WARNING",
                                     "glTF bake skins not supported")
        # matrix_basis = blender_object.convert_space(blender_bone, blender_bone.matrix, from_space='POSE',
        #                                             to_space='LOCAL')
    trans, rot, sca = gltf2_blender_extract.decompose_transition(
        gltf2_blender_math.multiply(correction_matrix_local, matrix_basis),
        'JOINT', export_settings)
    translation, rotation, scale = (None, None, None)
    if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0:
        translation = [trans[0], trans[1], trans[2]]
    if rot[0] != 0.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 1.0:
        rotation = [rot[0], rot[1], rot[2], rot[3]]
    if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0:
        scale = [sca[0], sca[1], sca[2]]

    # traverse into children
    children = []
    for bone in blender_bone.children:
        children.append(gather_joint(bone, export_settings))

    # finally add to the joints array containing all the joints in the hierarchy
    return gltf2_io.Node(camera=None,
                         children=children,
                         extensions=None,
                         extras=None,
                         matrix=None,
                         mesh=None,
                         name=blender_bone.name,
                         rotation=rotation,
                         scale=scale,
                         skin=None,
                         translation=translation,
                         weights=None)
Ejemplo n.º 2
0
    def __collect_matrices(bone):
        inverse_bind_matrix = gltf2_blender_math.multiply(
            axis_basis_change,
            gltf2_blender_math.multiply(blender_object.matrix_world,
                                        bone.bone.matrix_local)).inverted()
        matrices.append(inverse_bind_matrix)

        for child in bone.children:
            __collect_matrices(child)
Ejemplo n.º 3
0
def gather_joint(blender_object, blender_bone, export_settings):
    """
    Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes.

    :param blender_bone: a blender PoseBone
    :param export_settings: the settings for this export
    :return: a glTF2 node (acting as a joint)
    """
    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)))

    # extract bone transform
    if blender_bone.parent is None:
        correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, blender_bone.bone.matrix_local)
    else:
        correction_matrix_local = gltf2_blender_math.multiply(
            blender_bone.parent.bone.matrix_local.inverted(), blender_bone.bone.matrix_local)
    matrix_basis = blender_bone.matrix_basis
    trans, rot, sca = gltf2_blender_extract.decompose_transition(
        gltf2_blender_math.multiply(correction_matrix_local, matrix_basis), export_settings)
    translation, rotation, scale = (None, None, None)
    if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0:
        translation = [trans[0], trans[1], trans[2]]
    if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0:
        rotation = [rot[1], rot[2], rot[3], rot[0]]
    if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0:
        scale = [sca[0], sca[1], sca[2]]

    # traverse into children
    children = []

    if export_settings["gltf_def_bones"] is False:
        for bone in blender_bone.children:
            children.append(gather_joint(blender_object, bone, export_settings))
    else:
        _, children_, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_bone.id_data)
        if blender_bone.name in children_.keys():
            for bone in children_[blender_bone.name]:
                children.append(gather_joint(blender_object, blender_bone.id_data.pose.bones[bone], export_settings))

    # finally add to the joints array containing all the joints in the hierarchy
    return gltf2_io.Node(
        camera=None,
        children=children,
        extensions=None,
        extras=__gather_extras(blender_bone, export_settings),
        matrix=None,
        mesh=None,
        name=blender_bone.name,
        rotation=rotation,
        scale=scale,
        skin=None,
        translation=translation,
        weights=None
    )
def gather_joint(blender_bone, export_settings):
    """
    Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes.

    :param blender_bone: a blender PoseBone
    :param export_settings: the settings for this export
    :return: a glTF2 node (acting as a joint)
    """
    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)))

    # extract bone transform
    if blender_bone.parent is None:
        correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, blender_bone.bone.matrix_local)
    else:
        correction_matrix_local = gltf2_blender_math.multiply(
            blender_bone.parent.bone.matrix_local.inverted(), blender_bone.bone.matrix_local)
    matrix_basis = blender_bone.matrix_basis
    if export_settings[gltf2_blender_export_keys.BAKE_SKINS]:
        gltf2_io_debug.print_console("WARNING", "glTF bake skins not supported")
        # matrix_basis = blender_object.convert_space(blender_bone, blender_bone.matrix, from_space='POSE',
        #                                             to_space='LOCAL')
    trans, rot, sca = gltf2_blender_extract.decompose_transition(
        gltf2_blender_math.multiply(correction_matrix_local, matrix_basis), export_settings)
    translation, rotation, scale = (None, None, None)
    if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0:
        translation = [trans[0], trans[1], trans[2]]
    if rot[0] != 0.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 1.0:
        rotation = [rot[0], rot[1], rot[2], rot[3]]
    if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0:
        scale = [sca[0], sca[1], sca[2]]

    # traverse into children
    children = []
    for bone in blender_bone.children:
        children.append(gather_joint(bone, export_settings))

    # finally add to the joints array containing all the joints in the hierarchy
    return gltf2_io.Node(
        camera=None,
        children=children,
        extensions=None,
        extras=None,
        matrix=None,
        mesh=None,
        name=blender_bone.name,
        rotation=rotation,
        scale=scale,
        skin=None,
        translation=translation,
        weights=None
    )
Ejemplo n.º 5
0
    def __collect_matrices(bone):
        inverse_bind_matrix = gltf2_blender_math.multiply(
            axis_basis_change,
            gltf2_blender_math.multiply(blender_object.matrix_world,
                                        bone.bone.matrix_local)).inverted()
        matrices.append(inverse_bind_matrix)

        if export_settings['gltf_def_bones'] is False:
            for child in bone.children:
                __collect_matrices(child)
        else:
            if bone.name in children_.keys():
                for child in children_[bone.name]:
                    __collect_matrices(blender_object.pose.bones[child])
Ejemplo n.º 6
0
def __gather_inverse_bind_matrices(blender_object, export_settings):
    inverse_matrices = []

    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)))

    # # artificial torso, as needed by glTF
    # inverse_bind_matrix = blender_object.matrix_world.inverted() * axis_basis_change.inverted()
    # for column in range(0, 4):
    #     for row in range(0, 4):
    #         inverse_matrices.append(inverse_bind_matrix[row][column])

    #
    for blender_bone in blender_object.pose.bones:
        inverse_bind_matrix = gltf2_blender_math.multiply(
            axis_basis_change, blender_bone.bone.matrix_local)
        bind_shape_matrix = gltf2_blender_math.multiply(
            gltf2_blender_math.multiply(
                axis_basis_change, blender_object.matrix_world.inverted()),
            axis_basis_change.inverted())

        inverse_bind_matrix = gltf2_blender_math.multiply(
            inverse_bind_matrix.inverted(), bind_shape_matrix)
        for column in range(0, 4):
            for row in range(0, 4):
                inverse_matrices.append(inverse_bind_matrix[row][column])

    binary_data = gltf2_io_binary_data.BinaryData.from_list(
        inverse_matrices, gltf2_io_constants.ComponentType.Float)
    return gltf2_io.Accessor(
        buffer_view=binary_data,
        byte_offset=None,
        component_type=gltf2_io_constants.ComponentType.Float,
        count=len(inverse_matrices) //
        gltf2_io_constants.DataType.num_elements(
            gltf2_io_constants.DataType.Mat4),
        extensions=None,
        extras=None,
        max=None,
        min=None,
        name=None,
        normalized=None,
        sparse=None,
        type=gltf2_io_constants.DataType.Mat4)
def __gather_inverse_bind_matrices(blender_object, mesh_object, export_settings):
    inverse_matrices = []

    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)))

    # # artificial torso, as needed by glTF
    # inverse_bind_matrix = blender_object.matrix_world.inverted() * axis_basis_change.inverted()
    # for column in range(0, 4):
    #     for row in range(0, 4):
    #         inverse_matrices.append(inverse_bind_matrix[row][column])

    #
    for blender_bone in blender_object.pose.bones:
        matrix_world = gltf2_blender_math.multiply(blender_object.matrix_world, mesh_object.matrix_world.inverted())
        inverse_bind_matrix = gltf2_blender_math.multiply(
            axis_basis_change,
            gltf2_blender_math.multiply(
                matrix_world,
                blender_bone.bone.matrix_local
            )
        ).inverted()

        for column in range(0, 4):
            for row in range(0, 4):
                inverse_matrices.append(inverse_bind_matrix[row][column])

    binary_data = gltf2_io_binary_data.BinaryData.from_list(inverse_matrices, gltf2_io_constants.ComponentType.Float)
    return gltf2_blender_gather_accessors.gather_accessor(
        binary_data,
        gltf2_io_constants.ComponentType.Float,
        len(inverse_matrices) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Mat4),
        None,
        None,
        gltf2_io_constants.DataType.Mat4,
        export_settings
    )
Ejemplo n.º 8
0
def __gather_children(blender_object, blender_scene, export_settings):
    children = []
    # standard children
    for _child_object in blender_object.children:
        if _child_object.parent_bone:
            # this is handled further down,
            # as the object should be a child of the specific bone,
            # not the Armature object
            continue

        child_object = _child_object.proxy if _child_object.proxy else _child_object

        node = gather_node(
            child_object,
            child_object.library.name if child_object.library else None,
            blender_scene, None, export_settings)
        if node is not None:
            children.append(node)
    # blender dupli objects
    if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection:
        for dupli_object in blender_object.instance_collection.objects:
            if dupli_object.parent is not None:
                continue
            if dupli_object.type == "ARMATURE":
                continue  # There is probably a proxy
            node = gather_node(
                dupli_object,
                dupli_object.library.name if dupli_object.library else None,
                blender_scene, blender_object.name, export_settings)
            if node is not None:
                children.append(node)

    # blender bones
    if blender_object.type == "ARMATURE":
        root_joints = []
        if export_settings["gltf_def_bones"] is False:
            bones = blender_object.pose.bones
        else:
            bones, _, _ = gltf2_blender_gather_skins.get_bone_tree(
                None, blender_object)
            bones = [blender_object.pose.bones[b.name] for b in bones]
        for blender_bone in bones:
            if not blender_bone.parent:
                joint = gltf2_blender_gather_joints.gather_joint(
                    blender_object, blender_bone, export_settings)
                children.append(joint)
                root_joints.append(joint)
        # handle objects directly parented to bones
        direct_bone_children = [
            child for child in blender_object.children if child.parent_bone
        ]

        def find_parent_joint(joints, name):
            for joint in joints:
                if joint.name == name:
                    return joint
                parent_joint = find_parent_joint(joint.children, name)
                if parent_joint:
                    return parent_joint
            return None

        for child in direct_bone_children:
            # find parent joint
            parent_joint = find_parent_joint(root_joints, child.parent_bone)
            if not parent_joint:
                continue
            child_node = gather_node(child, None, None, None, export_settings)
            if child_node is None:
                continue
            blender_bone = blender_object.pose.bones[parent_joint.name]
            # fix rotation
            if export_settings[gltf2_blender_export_keys.YUP]:
                rot = child_node.rotation
                if rot is None:
                    rot = [0, 0, 0, 1]

                rot_quat = Quaternion(rot)
                axis_basis_change = 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)))
                mat = gltf2_blender_math.multiply(child.matrix_parent_inverse,
                                                  child.matrix_basis)
                mat = gltf2_blender_math.multiply(mat, axis_basis_change)

                _, rot_quat, _ = mat.decompose()
                child_node.rotation = [
                    rot_quat[1], rot_quat[2], rot_quat[3], rot_quat[0]
                ]

            # fix translation (in blender bone's tail is the origin for children)
            trans, _, _ = child.matrix_local.decompose()
            if trans is None:
                trans = [0, 0, 0]
            # bones go down their local y axis
            if blender_bone.matrix.to_scale()[1] >= 1e-6:
                bone_tail = [
                    0, blender_bone.length / blender_bone.matrix.to_scale()[1],
                    0
                ]
            else:
                bone_tail = [0, 0, 0]  # If scale is 0, tail == head
            child_node.translation = [
                trans[idx] + bone_tail[idx] for idx in range(3)
            ]

            parent_joint.children.append(child_node)

    return children
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
    )
def __gather_children(blender_object, blender_scene, export_settings):
    children = []
    # standard children
    for child_object in blender_object.children:
        if child_object.parent_bone:
            # this is handled further down,
            # as the object should be a child of the specific bone,
            # not the Armature object
            continue
        node = gather_node(child_object, blender_scene, export_settings)
        if node is not None:
            children.append(node)
    # blender dupli objects
    if bpy.app.version < (2, 80, 0):
        if blender_object.dupli_type == 'GROUP' and blender_object.dupli_group:
            for dupli_object in blender_object.dupli_group.objects:
                node = gather_node(dupli_object, blender_scene,
                                   export_settings)
                if node is not None:
                    children.append(node)
    else:
        if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection:
            for dupli_object in blender_object.instance_collection.objects:
                node = gather_node(dupli_object, blender_scene,
                                   export_settings)
                if node is not None:
                    children.append(node)

    # blender bones
    if blender_object.type == "ARMATURE":
        root_joints = []
        for blender_bone in blender_object.pose.bones:
            if not blender_bone.parent:
                joint = gltf2_blender_gather_joints.gather_joint(
                    blender_bone, export_settings)
                children.append(joint)
                root_joints.append(joint)
        # handle objects directly parented to bones
        direct_bone_children = [
            child for child in blender_object.children if child.parent_bone
        ]

        def find_parent_joint(joints, name):
            for joint in joints:
                if joint.name == name:
                    return joint
                parent_joint = find_parent_joint(joint.children, name)
                if parent_joint:
                    return parent_joint
            return None

        for child in direct_bone_children:
            # find parent joint
            parent_joint = find_parent_joint(root_joints, child.parent_bone)
            if not parent_joint:
                continue
            child_node = gather_node(child, None, export_settings)
            if child_node is None:
                continue
            blender_bone = blender_object.pose.bones[parent_joint.name]
            # fix rotation
            if export_settings[gltf2_blender_export_keys.YUP]:
                rot = child_node.rotation
                if rot is None:
                    rot = [0, 0, 0, 1]

                rot_quat = Quaternion(rot)
                axis_basis_change = 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)))
                mat = gltf2_blender_math.multiply(axis_basis_change,
                                                  child.matrix_basis)
                mat = gltf2_blender_math.multiply(child.matrix_parent_inverse,
                                                  mat)

                _, rot_quat, _ = mat.decompose()
                child_node.rotation = [
                    rot_quat[1], rot_quat[2], rot_quat[3], rot_quat[0]
                ]

            # fix translation (in blender bone's tail is the origin for children)
            trans, _, _ = child.matrix_local.decompose()
            if trans is None:
                trans = [0, 0, 0]
            # bones go down their local y axis
            bone_tail = [0, blender_bone.length, 0]
            child_node.translation = [
                trans[idx] + bone_tail[idx] for idx in range(3)
            ]

            parent_joint.children.append(child_node)

    return children
Ejemplo n.º 11
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
    )