Files
workinf_Blender_Wasm/blender-5.2.0/source/blender/animrig/intern/nla.cc
2026-08-12 04:47:48 -04:00

252 lines
8.5 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup animrig
*/
#include "BLI_bit_vector.hh"
#include "BLI_dynstr.h"
#include "BKE_animsys.h"
#include "BKE_fcurve.hh"
#include "RNA_access.hh"
#include "RNA_types.hh"
#include "BLT_translation.hh"
#include "ANIM_keyframing.hh"
#include "ANIM_nla.hh"
namespace blender::animrig::nla {
bool assign_action(NlaStrip &strip, Action &action, ID &animated_id)
{
if (!generic_assign_action(
animated_id, &action, strip.act, strip.action_slot_handle, strip.last_slot_identifier))
{
return false;
}
/* For the NLA, the auto slot selection gets one more fallback option (compared to the generic
* code). This is to support the following scenario:
*
* - Python script creates an Action, and adds some F-Curves via the legacy API.
* - This creates a slot 'XXSlot'.
* - The script creates multiple NLA strips for that Action.
* - The desired result is that these strips get the same Slot assigned as well.
*
* The generic code doesn't work for this. The first strip assignment would see the slot
* `XXSlot`, and because it has never been used, just use it. This would change its name to, for
* example, `OBSlot`. The second strip assignment would not see a 'virgin' slot, and thus not
* auto-select `OBSlot`. This behavior makes sense when assigning Actions in the Action editor
* (it shouldn't automatically pick the first slot of matching ID type), but for the NLA I
* (Sybren) feel that it could be a bit more 'enthusiastic' in auto-picking a slot.
*/
if (strip.action_slot_handle == Slot::unassigned && action.slots().size() == 1) {
Slot *first_slot = action.slot(0);
if (first_slot->is_suitable_for(animated_id)) {
const ActionSlotAssignmentResult result = assign_action_slot(strip, first_slot, animated_id);
BLI_assert_msg(result == ActionSlotAssignmentResult::OK,
"Assigning a slot that we know is suitable should work");
UNUSED_VARS_NDEBUG(result);
}
}
/* Regardless of slot auto-selection, the Action assignment worked just fine. */
return true;
}
void unassign_action(NlaStrip &strip, ID &animated_id)
{
const bool ok = generic_assign_action(
animated_id, nullptr, strip.act, strip.action_slot_handle, strip.last_slot_identifier);
BLI_assert_msg(ok, "Un-assigning an Action from an NLA strip should always work.");
UNUSED_VARS_NDEBUG(ok);
}
ActionSlotAssignmentResult assign_action_slot(NlaStrip &strip,
Slot *slot_to_assign,
ID &animated_id)
{
BLI_assert(strip.act);
return generic_assign_action_slot(slot_to_assign,
animated_id,
strip.act,
strip.action_slot_handle,
strip.last_slot_identifier);
}
ActionSlotAssignmentResult assign_action_slot_handle(NlaStrip &strip,
const slot_handle_t slot_handle,
ID &animated_id)
{
BLI_assert(strip.act);
Action &action = strip.act->wrap();
Slot *slot_to_assign = action.slot_for_handle(slot_handle);
return assign_action_slot(strip, slot_to_assign, animated_id);
}
/* Check indices that were intended to be remapped and report any failed remaps. */
static void get_keyframe_values_create_reports(ReportList *reports,
const PointerRNA &ptr,
const PropertyRNA *prop,
const int index,
const int count,
const bool force_all,
const BitSpan successful_remaps)
{
DynStr *ds_failed_indices = BLI_dynstr_new();
int total_failed = 0;
for (int i = 0; i < count; i++) {
const bool cur_index_evaluated = ELEM(index, i, -1) || force_all;
if (!cur_index_evaluated) {
/* `values[i]` was never intended to be remapped. */
continue;
}
if (successful_remaps[i]) {
/* `values[i]` successfully remapped. */
continue;
}
total_failed++;
/* Report that `values[i]` were intended to be remapped but failed remapping process. */
BLI_dynstr_appendf(ds_failed_indices, "%d, ", i);
}
if (total_failed == 0) {
BLI_dynstr_free(ds_failed_indices);
return;
}
char *str_failed_indices = BLI_dynstr_get_cstring(ds_failed_indices);
BLI_dynstr_free(ds_failed_indices);
BKE_reportf(reports,
RPT_WARNING,
"Could not insert %i keyframe(s) due to zero NLA influence, base value, or value "
"remapping failed: %s.%s for indices [%s]",
total_failed,
ptr.owner_id->name,
RNA_property_ui_name(prop),
str_failed_indices);
MEM_delete(str_failed_indices);
}
static BitVector<> nla_map_keyframe_values_and_generate_reports(
const MutableSpan<float> values,
const int index,
PointerRNA &ptr,
PropertyRNA &prop,
NlaKeyframingContext *nla_context,
const AnimationEvalContext *anim_eval_context,
ReportList *reports,
bool *force_all)
{
BitVector<> successful_remaps(values.size(), false);
BKE_animsys_nla_remap_keyframe_values(
nla_context, &ptr, &prop, values, index, anim_eval_context, force_all, successful_remaps);
get_keyframe_values_create_reports(
reports, ptr, &prop, index, values.size(), false, successful_remaps);
return successful_remaps;
}
bool insert_keyframe_direct(ReportList *reports,
PointerRNA ptr,
PropertyRNA *prop,
FCurve *fcu,
const AnimationEvalContext *anim_eval_context,
eBezTriple_KeyframeType keytype,
NlaKeyframingContext *nla_context,
eInsertKeyFlags flag)
{
if (fcu == nullptr) {
BKE_report(reports, RPT_ERROR, "No F-Curve to add keyframes to");
return false;
}
if (!BKE_fcurve_is_keyframable(*fcu)) {
BKE_report(reports, RPT_ERROR, "FCurve is not keyable. Cannot insert keyframes");
return false;
}
if ((ptr.owner_id == nullptr) && (ptr.data == nullptr)) {
BKE_report(
reports, RPT_ERROR, "No RNA pointer available to retrieve values for keyframing from");
return false;
}
if (prop == nullptr) {
PointerRNA tmp_ptr;
if (RNA_path_resolve_property(&ptr, fcu->rna_path, &tmp_ptr, &prop) == false) {
const char *idname = (ptr.owner_id) ? ptr.owner_id->name : RPT_("<No ID pointer>");
BKE_reportf(reports,
RPT_ERROR,
"Could not insert keyframe, as RNA path is invalid for the given ID (ID = %s, "
"path = %s)",
idname,
fcu->rna_path);
return false;
}
/* Property found, so overwrite 'ptr' to make later code easier. */
ptr = tmp_ptr;
}
/* Update F-Curve flags to ensure proper behavior for property type. */
update_autoflags_fcurve_direct(fcu, RNA_property_type(prop));
const int index = fcu->array_index;
const bool visual_keyframing = flag & INSERTKEY_MATRIX;
Vector<float> values = get_property_values(&ptr, prop, visual_keyframing);
BitVector<> successful_remaps = nla_map_keyframe_values_and_generate_reports(
values.as_mutable_span(),
index,
ptr,
*prop,
nla_context,
anim_eval_context,
reports,
nullptr);
float current_value = 0.0f;
if (index >= 0 && index < values.size()) {
current_value = values[index];
}
/* This happens if NLA rejects this insertion. */
if (!successful_remaps[index]) {
return false;
}
KeyframeSettings settings = get_keyframe_settings((flag & INSERTKEY_NO_USERPREF) == 0);
settings.keyframe_type = keytype;
const SingleKeyingResult result = insert_vert_fcurve(
fcu, {anim_eval_context->eval_time, current_value}, settings, flag);
if (result != SingleKeyingResult::SUCCESS) {
BKE_reportf(reports,
RPT_ERROR,
"Failed to insert keys on F-Curve with path '%s[%d]', ensure that it is not "
"locked or sampled, and try removing F-Modifiers",
fcu->rna_path,
fcu->array_index);
}
return result == SingleKeyingResult::SUCCESS;
}
} // namespace blender::animrig::nla