def __gather_node(channels: typing.Tuple[bpy.types.FCurve],
                  blender_object: bpy.types.Object,
                  export_settings,
                  bake_bone: typing.Union[str, None],
                  driver_obj
                  ) -> gltf2_io.Node:

    if driver_obj is not None:
        return gltf2_blender_gather_nodes.gather_node(driver_obj,
            driver_obj.library.name if driver_obj.library else None,
            None, None, export_settings)

    if blender_object.type == "ARMATURE":
        # TODO: get joint from fcurve data_path and gather_joint

        if bake_bone is not None:
            blender_bone = blender_object.pose.bones[bake_bone]
        else:
            blender_bone = blender_object.path_resolve(channels[0].data_path.rsplit('.', 1)[0])

        if isinstance(blender_bone, bpy.types.PoseBone):
            if export_settings["gltf_def_bones"] is False:
                obj = blender_object.proxy if blender_object.proxy else blender_object
                return gltf2_blender_gather_joints.gather_joint(obj, blender_bone, export_settings)
            else:
                bones, _, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_object)
                if blender_bone.name in [b.name for b in bones]:
                    obj = blender_object.proxy if blender_object.proxy else blender_object
                    return gltf2_blender_gather_joints.gather_jointb(obj, blender_bone, export_settings)

    return gltf2_blender_gather_nodes.gather_node(blender_object,
        blender_object.library.name if blender_object.library else None,
        None, None, export_settings)
Esempio n. 2
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
    )
Esempio 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)
    """
    translation, rotation, scale = __gather_trans_rot_scale(
        blender_object, blender_bone, export_settings)

    # 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
    node = 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)
    node.__blender_data = ('BONE', blender_object, blender_bone.name)

    export_user_extensions('gather_joint_hook', export_settings, node,
                           blender_bone)

    return node
Esempio n. 4
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
Esempio n. 5
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 = axis_basis_change @ blender_bone.bone.matrix_local
    else:
        correction_matrix_local = (
            blender_bone.parent.bone.matrix_local.inverted_safe() @
            blender_bone.bone.matrix_local
        )

    if (blender_bone.bone.use_inherit_rotation == False or blender_bone.bone.inherit_scale != "FULL") and blender_bone.parent != None:
        rest_mat = (blender_bone.parent.bone.matrix_local.inverted_safe() @ blender_bone.bone.matrix_local)
        matrix_basis = (rest_mat.inverted_safe() @ blender_bone.parent.matrix.inverted_safe() @ blender_bone.matrix)
    else:
        matrix_basis = blender_bone.matrix
        matrix_basis = blender_object.convert_space(pose_bone=blender_bone, matrix=matrix_basis, from_space='POSE', to_space='LOCAL')

    trans, rot, sca = (correction_matrix_local @ matrix_basis).decompose()
    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
    node = 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
    )

    export_user_extensions('gather_joint_hook', export_settings, node, blender_bone)

    return node
Esempio n. 6
0
def gather_animation_channels(
        blender_action: bpy.types.Action, blender_object: bpy.types.Object,
        export_settings) -> typing.List[gltf2_io.AnimationChannel]:
    channels = []

    # First calculate range of animation for baking
    # This is need if user set 'Force sampling' and in case we need to bake
    bake_range_start = None
    bake_range_end = None
    groups = __get_channel_groups(blender_action, blender_object,
                                  export_settings)
    # Note: channels has some None items only for SK if some SK are not animated
    for chans in groups:
        ranges = [channel.range() for channel in chans if channel is not None]
        if bake_range_start is None:
            bake_range_start = min([
                channel.range()[0] for channel in chans if channel is not None
            ])
        else:
            bake_range_start = min(
                bake_range_start,
                min([
                    channel.range()[0] for channel in chans
                    if channel is not None
                ]))
        if bake_range_end is None:
            bake_range_end = max([
                channel.range()[1] for channel in chans if channel is not None
            ])
        else:
            bake_range_end = max(
                bake_range_end,
                max([
                    channel.range()[1] for channel in chans
                    if channel is not None
                ]))

    if blender_object.type == "ARMATURE" and export_settings[
            'gltf_force_sampling'] is True:
        # We have to store sampled animation data for every deformation bones

        # Check that there are some anim in this action
        if bake_range_start is None:
            return []

        # Then bake all bones
        bones_to_be_animated = []
        if export_settings["gltf_def_bones"] is False:
            bones_to_be_animated = blender_object.data.bones
        else:
            bones_to_be_animated, _, _ = gltf2_blender_gather_skins.get_bone_tree(
                None, blender_object)
            bones_to_be_animated = [
                blender_object.pose.bones[b.name] for b in bones_to_be_animated
            ]

        for bone in bones_to_be_animated:
            for p in ["location", "rotation_quaternion", "scale"]:
                channel = __gather_animation_channel(
                    (), blender_object, export_settings, bone.name, p,
                    bake_range_start, bake_range_end, blender_action.name)
                channels.append(channel)
    else:
        for channel_group in __get_channel_groups(blender_action,
                                                  blender_object,
                                                  export_settings):
            channel_group_sorted = __get_channel_group_sorted(
                channel_group, blender_object)
            channel = __gather_animation_channel(
                channel_group_sorted, blender_object, export_settings, None,
                None, bake_range_start, bake_range_end, blender_action.name)
            if channel is not None:
                channels.append(channel)

    return channels
def gather_animation_channels(blender_action: bpy.types.Action,
                              blender_object: bpy.types.Object,
                              export_settings
                              ) -> typing.List[gltf2_io.AnimationChannel]:
    channels = []


    # First calculate range of animation for baking
    # This is need if user set 'Force sampling' and in case we need to bake
    bake_range_start = None
    bake_range_end = None
    groups = __get_channel_groups(blender_action, blender_object, export_settings)
    # Note: channels has some None items only for SK if some SK are not animated
    for chans in groups:
        ranges = [channel.range() for channel in chans  if channel is not None]
        if bake_range_start is None:
            bake_range_start = min([channel.range()[0] for channel in chans  if channel is not None])
        else:
            bake_range_start = min(bake_range_start, min([channel.range()[0] for channel in chans  if channel is not None]))
        if bake_range_end is None:
            bake_range_end = max([channel.range()[1] for channel in chans  if channel is not None])
        else:
            bake_range_end = max(bake_range_end, max([channel.range()[1] for channel in chans  if channel is not None]))


    if blender_object.type == "ARMATURE" and export_settings['gltf_force_sampling'] is True:
        # We have to store sampled animation data for every deformation bones

        # Check that there are some anim in this action
        if bake_range_start is None:
            return []

        # Then bake all bones
        bones_to_be_animated = []
        if export_settings["gltf_def_bones"] is False:
            bones_to_be_animated = blender_object.data.bones
        else:
            bones_to_be_animated, _, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_object)
            bones_to_be_animated = [blender_object.pose.bones[b.name] for b in bones_to_be_animated]

        for bone in bones_to_be_animated:
            for p in ["location", "rotation_quaternion", "scale"]:
                channel = __gather_animation_channel(
                    (),
                    blender_object,
                    export_settings,
                    bone.name,
                    p,
                    bake_range_start,
                    bake_range_end,
                    blender_action.name,
                    None)
                channels.append(channel)


        # Retrieve animation on armature object itself, if any
        fcurves_armature = __gather_armature_object_channel_groups(blender_action, blender_object, export_settings)
        for channel_group in fcurves_armature:
            # No need to sort on armature, that can't have SK
            if len(channel_group) == 0:
                # Only errors on channels, ignoring
                continue
            channel = __gather_animation_channel(channel_group, blender_object, export_settings, None, None, bake_range_start, bake_range_end, blender_action.name, None)
            if channel is not None:
                channels.append(channel)


        # Retrieve channels for drivers, if needed
        drivers_to_manage = gltf2_blender_gather_drivers.get_sk_drivers(blender_object)
        for obj, fcurves in drivers_to_manage:
            channel = __gather_animation_channel(
                fcurves,
                blender_object,
                export_settings,
                None,
                None,
                bake_range_start,
                bake_range_end,
                blender_action.name,
                obj)
            channels.append(channel)

    else:
        for channel_group in __get_channel_groups(blender_action, blender_object, export_settings):
            channel_group_sorted = __get_channel_group_sorted(channel_group, blender_object)
            if len(channel_group_sorted) == 0:
                # Only errors on channels, ignoring
                continue
            channel = __gather_animation_channel(channel_group_sorted, blender_object, export_settings, None, None, bake_range_start, bake_range_end, blender_action.name, None)
            if channel is not None:
                channels.append(channel)


    # resetting driver caches
    gltf2_blender_gather_drivers.get_sk_driver_values.reset_cache()
    gltf2_blender_gather_drivers.get_sk_drivers.reset_cache()

    return channels
Esempio 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

            parent_joint.children.append(child_node)

    return children