def get_bone_matrix(blender_obj_uuid_if_armature: typing.Optional[str],
                     channels: typing.Tuple[bpy.types.FCurve],
                     bake_bone: typing.Union[str, None],
                     bake_channel: typing.Union[str, None],
                     bake_range_start,
                     bake_range_end,
                     action_name: str,
                     current_frame: int,
                     step: int,
                     export_settings
                     ):

    blender_object_if_armature = export_settings['vtree'].nodes[blender_obj_uuid_if_armature].blender_object if blender_obj_uuid_if_armature is not None else None
    data = {}

    # Always using bake_range, because some bones may need to be baked,
    # even if user didn't request it

    start_frame = bake_range_start
    end_frame = bake_range_end


    frame = start_frame
    while frame <= end_frame:
        data[frame] = {}
        bpy.context.scene.frame_set(int(frame))
        bones = export_settings['vtree'].get_all_bones(blender_obj_uuid_if_armature)

        for bone_uuid in bones:
            blender_bone = export_settings['vtree'].nodes[bone_uuid].blender_bone

            if export_settings['vtree'].nodes[bone_uuid].parent_uuid is not None and export_settings['vtree'].nodes[export_settings['vtree'].nodes[bone_uuid].parent_uuid].blender_type == VExportNode.BONE:
                blender_bone_parent = export_settings['vtree'].nodes[export_settings['vtree'].nodes[bone_uuid].parent_uuid].blender_bone
                rest_mat = blender_bone_parent.bone.matrix_local.inverted_safe() @ blender_bone.bone.matrix_local
                matrix = rest_mat.inverted_safe() @ blender_bone_parent.matrix.inverted_safe() @ blender_bone.matrix
            else:
                if blender_bone.parent is None:
                    matrix = blender_bone.bone.matrix_local.inverted_safe() @ blender_bone.matrix
                else:
                    # Bone has a parent, but in export, after filter, is at root of armature
                    matrix = blender_bone.matrix.copy()

            data[frame][blender_bone.name] = matrix


        # If some drivers must be evaluated, do it here, to avoid to have to change frame by frame later
        drivers_to_manage = get_sk_drivers(blender_obj_uuid_if_armature, export_settings)
        for dr_obj_uuid, dr_fcurves in drivers_to_manage:
            vals = get_sk_driver_values(dr_obj_uuid, frame, dr_fcurves, export_settings)

        frame += step

    return data
def get_bone_matrix(
        blender_object_if_armature: typing.Optional[bpy.types.Object],
        channels: typing.Tuple[bpy.types.FCurve],
        bake_bone: typing.Union[str, None],
        bake_channel: typing.Union[str, None], bake_range_start,
        bake_range_end, action_name: str, current_frame: int, step: int):

    data = {}

    # Always using bake_range, because some bones may need to be baked,
    # even if user didn't request it

    start_frame = bake_range_start
    end_frame = bake_range_end

    frame = start_frame
    while frame <= end_frame:
        data[frame] = {}
        # we need to bake in the constraints
        bpy.context.scene.frame_set(frame)
        for pbone in blender_object_if_armature.pose.bones:
            if bake_bone is None:
                matrix = pbone.matrix_basis.copy()
            else:
                if (pbone.bone.use_inherit_rotation == False
                        or pbone.bone.inherit_scale != "FULL"
                    ) and pbone.parent != None:
                    rest_mat = (pbone.parent.bone.matrix_local.inverted_safe()
                                @ pbone.bone.matrix_local)
                    matrix = (
                        rest_mat.inverted_safe()
                        @ pbone.parent.matrix.inverted_safe() @ pbone.matrix)
                else:
                    matrix = pbone.matrix
                    matrix = blender_object_if_armature.convert_space(
                        pose_bone=pbone,
                        matrix=matrix,
                        from_space='POSE',
                        to_space='LOCAL')

            data[frame][pbone.name] = matrix

        # If some drivers must be evaluated, do it here, to avoid to have to change frame by frame later
        obj_driver = blender_object_if_armature.proxy if blender_object_if_armature.proxy else blender_object_if_armature
        drivers_to_manage = get_sk_drivers(obj_driver)
        for dr_obj, dr_fcurves in drivers_to_manage:
            vals = get_sk_driver_values(dr_obj, frame, dr_fcurves)

        frame += step

    return data
コード例 #3
0
def get_bone_matrix(
        blender_object_if_armature: typing.Optional[bpy.types.Object],
        channels: typing.Tuple[bpy.types.FCurve],
        bake_bone: typing.Union[str, None],
        bake_channel: typing.Union[str, None], bake_range_start,
        bake_range_end, action_name: str, current_frame: int, step: int):

    data = {}

    # Always using bake_range, because some bones may need to be baked,
    # even if user didn't request it

    start_frame = bake_range_start
    end_frame = bake_range_end

    frame = start_frame
    while frame <= end_frame:
        data[frame] = {}
        # we need to bake in the constraints
        bpy.context.scene.frame_set(frame)
        for pbone in blender_object_if_armature.pose.bones:
            if bake_bone is None:
                matrix = pbone.matrix_basis
            else:
                matrix = pbone.matrix
                if bpy.app.version < (2, 80, 0):
                    matrix = blender_object_if_armature.convert_space(
                        pbone, matrix, 'POSE', 'LOCAL')
                else:
                    matrix = blender_object_if_armature.convert_space(
                        pose_bone=pbone,
                        matrix=matrix,
                        from_space='POSE',
                        to_space='LOCAL')
            data[frame][pbone.name] = matrix

        # If some drivers must be evaluated, do it here, to avoid to have to change frame by frame later
        drivers_to_manage = get_sk_drivers(blender_object_if_armature)
        for dr_obj, dr_fcurves in drivers_to_manage:
            vals = get_sk_driver_values(dr_obj, frame, dr_fcurves)

        frame += step

    return data
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
コード例 #5
0
def gather_animation_channels(
        obj_uuid: int, blender_action: bpy.types.Action,
        export_settings) -> typing.List[gltf2_io.AnimationChannel]:
    channels = []

    blender_object = export_settings['vtree'].nodes[obj_uuid].blender_object

    # 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
    force_range = False
    # If range is manually set, use it. Else, calculate it
    if blender_action.use_frame_range is True:
        bake_range_start = blender_action.frame_start
        bake_range_end = blender_action.frame_end
        force_range = True  # keyframe_points is read-only, we cant restrict here
    else:
        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 = []
        bones_uuid = export_settings["vtree"].get_all_bones(obj_uuid)
        bones_to_be_animated = [
            blender_object.pose.bones[
                export_settings["vtree"].nodes[b].blender_bone.name]
            for b in bones_uuid
        ]

        list_of_animated_bone_channels = []
        for channel_group in __get_channel_groups(blender_action,
                                                  blender_object,
                                                  export_settings):
            channel_group_sorted = __get_channel_group_sorted(
                channel_group, blender_object)
            list_of_animated_bone_channels.extend([
                (gltf2_blender_get.get_object_from_datapath(
                    blender_object, get_target_object_path(i.data_path)).name,
                 get_target_property_name(i.data_path)) for i in channel_group
            ])

        for bone in bones_to_be_animated:
            for p in ["location", "rotation_quaternion", "scale"]:
                channel = gather_animation_channel(
                    obj_uuid, (), export_settings, bone.name, p,
                    bake_range_start, bake_range_end, force_range,
                    blender_action.name, None, (bone.name, p)
                    in list_of_animated_bone_channels)
                if channel is not 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(obj_uuid, channel_group,
                                               export_settings, None, None,
                                               bake_range_start,
                                               bake_range_end, force_range,
                                               blender_action.name, None, True)
            if channel is not None:
                channels.append(channel)

        # Retrieve channels for drivers, if needed
        drivers_to_manage = gltf2_blender_gather_drivers.get_sk_drivers(
            obj_uuid, export_settings)
        for obj_driver_uuid, fcurves in drivers_to_manage:
            channel = gather_animation_channel(obj_uuid, fcurves,
                                               export_settings, None, None,
                                               bake_range_start,
                                               bake_range_end, force_range,
                                               blender_action.name,
                                               obj_driver_uuid, True)
            if channel is not None:
                channels.append(channel)

    else:
        done_paths = []
        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(obj_uuid, channel_group_sorted,
                                               export_settings, None, None,
                                               bake_range_start,
                                               bake_range_end, force_range,
                                               blender_action.name, None, True)
            if channel is not None:
                channels.append(channel)

            # Store already done channel path
            target = [c for c in channel_group_sorted
                      if c is not None][0].data_path.split('.')[-1]
            path = {
                "delta_location": "location",
                "delta_rotation_euler": "rotation_quaternion",
                "location": "location",
                "rotation_axis_angle": "rotation_quaternion",
                "rotation_euler": "rotation_quaternion",
                "rotation_quaternion": "rotation_quaternion",
                "scale": "scale",
                "value": "weights"
            }.get(target)
            if path is not None:
                done_paths.append(path)
        done_paths = list(set(done_paths))

        if export_settings['gltf_selected'] is True and export_settings[
                'vtree'].tree_troncated is True:
            start_frame = min(
                [v[0] for v in [a.frame_range for a in bpy.data.actions]])
            end_frame = max(
                [v[1] for v in [a.frame_range for a in bpy.data.actions]])
            to_be_done = ['location', 'rotation_quaternion', 'scale']
            to_be_done = [c for c in to_be_done if c not in done_paths]

            # In case of weight action, do nothing.
            # If there is only weight --> TRS is already managed at first
            if not (len(done_paths) == 1 and 'weights' in done_paths):
                for p in to_be_done:
                    channel = gather_animation_channel(
                        obj_uuid,
                        (),
                        export_settings,
                        None,
                        p,
                        start_frame,
                        end_frame,
                        force_range,
                        blender_action.name,
                        None,
                        False  #If Object is not animated, don't keep animation for this channel
                    )

                    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()
    # resetting bone caches
    gltf2_blender_gather_animation_sampler_keyframes.get_bone_matrix.reset_cache(
    )

    return channels