This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/armature_pose.cc
Sybren A. Stüvel 28dc07a153 Animation: add function to blend Action into pose
Add function `BKE_pose_apply_action_blend()`, which blends a given
Action into current pose. The Action is evaluated at a specified frame,
and the result is applied to the armature's pose.

A blend factor can be given to blend between the current pose and the
one in the Action. Quaternions are interpolated with SLERP; it is
assumed that their FCurves are consecutively stored in the Action.

This function will be used in the upcoming new Pose Library.
2021-07-15 16:12:36 +02:00

181 lines
5.8 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2015 Blender Foundation.
* All rights reserved.
*
* Defines and code for core node types
*/
/** \file
* \ingroup bke
*/
#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BLI_function_ref.hh"
#include "BLI_set.hh"
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "RNA_access.h"
namespace {
using BoneNameSet = blender::Set<std::string>;
using ActionApplier =
blender::FunctionRef<void(PointerRNA *, bAction *, const AnimationEvalContext *)>;
// Forward declarations.
BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose);
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names);
void pose_apply_restore_fcurves(bAction *action);
void pose_apply(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context,
ActionApplier applier);
} // namespace
void BKE_pose_apply_action_selected_bones(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context)
{
auto evaluate_and_apply =
[](PointerRNA *ptr, bAction *act, const AnimationEvalContext *anim_eval_context) {
animsys_evaluate_action(ptr, act, anim_eval_context, false);
};
pose_apply(ob, action, anim_eval_context, evaluate_and_apply);
}
void BKE_pose_apply_action_all_bones(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context)
{
PointerRNA pose_owner_ptr;
RNA_id_pointer_create(&ob->id, &pose_owner_ptr);
animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false);
}
void BKE_pose_apply_action_blend(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context,
const float blend_factor)
{
auto evaluate_and_blend = [blend_factor](PointerRNA *ptr,
bAction *act,
const AnimationEvalContext *anim_eval_context) {
animsys_blend_in_action(ptr, act, anim_eval_context, blend_factor);
};
pose_apply(ob, action, anim_eval_context, evaluate_and_blend);
}
namespace {
void pose_apply(struct Object *ob,
struct bAction *action,
struct AnimationEvalContext *anim_eval_context,
ActionApplier applier)
{
bPose *pose = ob->pose;
if (pose == nullptr) {
return;
}
const bArmature *armature = (bArmature *)ob->data;
const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose);
const bool limit_to_selected_bones = !selected_bone_names.is_empty();
if (limit_to_selected_bones) {
/* Mute all FCurves that are not associated with selected bones. This separates the concept of
* bone selection from the FCurve evaluation code. */
pose_apply_disable_fcurves_for_unselected_bones(action, selected_bone_names);
}
/* Apply the Action. */
PointerRNA pose_owner_ptr;
RNA_id_pointer_create(&ob->id, &pose_owner_ptr);
applier(&pose_owner_ptr, action, anim_eval_context);
if (limit_to_selected_bones) {
pose_apply_restore_fcurves(action);
}
}
BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose)
{
BoneNameSet selected_bone_names;
bool all_bones_selected = true;
bool no_bones_selected = true;
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
const bool is_selected = PBONE_SELECTED(armature, pchan->bone);
all_bones_selected &= is_selected;
no_bones_selected &= !is_selected;
if (is_selected) {
/* Bone names are unique, so no need to check for duplicates. */
selected_bone_names.add_new(pchan->name);
}
}
/* If no bones are selected, act as if all are. */
if (all_bones_selected || no_bones_selected) {
return BoneNameSet(); /* An empty set means "ignore bone selection". */
}
return selected_bone_names;
}
void pose_apply_restore_fcurves(bAction *action)
{
/* TODO(Sybren): Restore the FCurve flags, instead of just erasing the 'disabled' flag. */
LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
fcu->flag &= ~FCURVE_DISABLED;
}
}
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names)
{
LISTBASE_FOREACH (FCurve *, fcu, &action->curves) {
if (!fcu->rna_path || !strstr(fcu->rna_path, "pose.bones[")) {
continue;
}
/* Get bone name, and check if this bone is selected. */
char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
if (!bone_name) {
continue;
}
const bool is_selected = selected_bone_names.contains(bone_name);
MEM_freeN(bone_name);
if (is_selected) {
continue;
}
fcu->flag |= FCURVE_DISABLED;
}
}
} // namespace