Add Chromium-only Blender WebEngine parity work
This commit is contained in:
366
blender-5.2.0/source/blender/animrig/intern/animdata.cc
Normal file
366
blender-5.2.0/source/blender/animrig/intern/animdata.cc
Normal file
@@ -0,0 +1,366 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup animrig
|
||||
*/
|
||||
#include "ANIM_action.hh"
|
||||
#include "ANIM_animdata.hh"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_fcurve.hh"
|
||||
#include "BKE_key.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_node.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
namespace blender::animrig {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public F-Curves API
|
||||
* \{ */
|
||||
|
||||
/* Find the users of the given ID within the objects of `bmain` and add non-duplicates to the end
|
||||
* of `related_ids`. */
|
||||
static void add_object_data_users(const Main &bmain, const ID &id, Vector<ID *> &related_ids)
|
||||
{
|
||||
if (ID_REAL_USERS(&id) != 1) {
|
||||
/* Only find objects if this ID is only used once. */
|
||||
return;
|
||||
}
|
||||
|
||||
Object *ob;
|
||||
ID *object_id;
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (&bmain.objects, object_id) {
|
||||
ob = reinterpret_cast<Object *>(object_id);
|
||||
if (ob->data != &id) {
|
||||
continue;
|
||||
}
|
||||
related_ids.append_non_duplicates(&ob->id);
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
}
|
||||
|
||||
Vector<ID *> find_related_ids(Main &bmain, ID &id)
|
||||
{
|
||||
Vector<ID *> related_ids({&id});
|
||||
|
||||
/* `related_ids` can grow during an iteration if the ID of the current iteration has associated
|
||||
* code that defines relationships. */
|
||||
for (int i = 0; i < related_ids.size(); i++) {
|
||||
ID *related_id = related_ids[i];
|
||||
|
||||
if (related_id->flag & ID_FLAG_EMBEDDED_DATA) {
|
||||
/* No matter the type of embedded ID, their owner can always be added to the related IDs. */
|
||||
|
||||
/* User counting is irrelevant for the logic here, because embedded IDs cannot be shared.
|
||||
* Embedded IDs do exist (sometimes) with a non-zero user count, hence the assertion that the
|
||||
* user count is not greater than 1. */
|
||||
BLI_assert(ID_REAL_USERS(related_id) <= 1);
|
||||
ID *owner_id = BKE_id_owner_get(related_id);
|
||||
/* Embedded IDs should always have an owner. */
|
||||
BLI_assert(owner_id != nullptr);
|
||||
related_ids.append_non_duplicates(owner_id);
|
||||
}
|
||||
|
||||
/* No action found on current ID, add related IDs to the ID Vector. */
|
||||
switch (GS(related_id->name)) {
|
||||
case ID_OB: {
|
||||
Object *ob = reinterpret_cast<Object *>(related_id);
|
||||
if (!ob->data) {
|
||||
break;
|
||||
}
|
||||
ID *data = ob->data;
|
||||
if (ID_REAL_USERS(data) == 1) {
|
||||
related_ids.append_non_duplicates(data);
|
||||
}
|
||||
for (ParticleSystem &particle_system : ob->particlesystem) {
|
||||
if (!particle_system.part) {
|
||||
continue;
|
||||
}
|
||||
if (ID_REAL_USERS(&particle_system.part->id) != 1) {
|
||||
continue;
|
||||
}
|
||||
related_ids.append_non_duplicates(&particle_system.part->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_KE: {
|
||||
/* Shape-keys. */
|
||||
Key *key = reinterpret_cast<Key *>(related_id);
|
||||
/* Shape-keys are not embedded but there is currently no way to reuse them. */
|
||||
BLI_assert(ID_REAL_USERS(related_id) == 1);
|
||||
related_ids.append_non_duplicates(key->from);
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_MA: {
|
||||
/* Explicitly not relating materials and material users. */
|
||||
Material *mat = reinterpret_cast<Material *>(related_id);
|
||||
if (mat->nodetree && ID_REAL_USERS(&mat->nodetree->id) == 1) {
|
||||
related_ids.append_non_duplicates(&mat->nodetree->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_PA: {
|
||||
if (ID_REAL_USERS(related_id) != 1) {
|
||||
continue;
|
||||
}
|
||||
Object *ob;
|
||||
ID *object_id;
|
||||
/* Find users of this particle setting. */
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (&bmain.objects, object_id) {
|
||||
ob = reinterpret_cast<Object *>(object_id);
|
||||
bool object_uses_particle_settings = false;
|
||||
for (ParticleSystem &particle_system : ob->particlesystem) {
|
||||
if (!particle_system.part) {
|
||||
continue;
|
||||
}
|
||||
if (&particle_system.part->id != related_id) {
|
||||
continue;
|
||||
}
|
||||
object_uses_particle_settings = true;
|
||||
break;
|
||||
}
|
||||
if (object_uses_particle_settings) {
|
||||
related_ids.append_non_duplicates(&ob->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
/* Just check if the ID is used as object data somewhere. */
|
||||
add_object_data_users(bmain, *related_id, related_ids);
|
||||
bNodeTree *node_tree = bke::node_tree_from_id(related_id);
|
||||
if (node_tree && ID_REAL_USERS(&node_tree->id) == 1) {
|
||||
related_ids.append_non_duplicates(&node_tree->id);
|
||||
}
|
||||
|
||||
Key *key = BKE_key_from_id(related_id);
|
||||
if (key) {
|
||||
/* No check for multi user because the shape-key cannot be shared. */
|
||||
BLI_assert(ID_REAL_USERS(&key->id) == 1);
|
||||
related_ids.append_non_duplicates(&key->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return related_ids;
|
||||
}
|
||||
|
||||
/* Find an action on an ID that is related to the given ID. Related things are e.g. Object<->Data,
|
||||
* Mesh<->Material and so on. */
|
||||
static bAction *find_related_action(Main &bmain, ID &id)
|
||||
{
|
||||
Vector<ID *> related_ids = find_related_ids(bmain, id);
|
||||
|
||||
for (ID *related_id : related_ids) {
|
||||
Action *action = get_action(*related_id);
|
||||
if (action && BKE_id_is_editable(&bmain, &action->id)) {
|
||||
/* Returning the first action found means highest priority has the action closest in the
|
||||
* relationship graph. */
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bAction *id_action_ensure(Main *bmain, ID *id)
|
||||
{
|
||||
AnimData *adt = BKE_animdata_ensure_id(id);
|
||||
if (adt == nullptr) {
|
||||
printf("ERROR: data-block type is not animatable (ID = %s)\n", (id) ? (id->name) : "<None>");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* init action if none available yet */
|
||||
/* TODO: need some wizardry to handle NLA stuff correct */
|
||||
if (adt->action == nullptr) {
|
||||
bAction *action = find_related_action(*bmain, *id);
|
||||
|
||||
if (action == nullptr) {
|
||||
/* init action name from name of ID block */
|
||||
char actname[sizeof(id->name) - 2];
|
||||
if (id->flag & ID_FLAG_EMBEDDED_DATA) {
|
||||
/* When the ID is embedded, use the name of the owner ID for clarity. */
|
||||
ID *owner_id = BKE_id_owner_get(id);
|
||||
/* If the ID is embedded it should have an owner. */
|
||||
BLI_assert(owner_id != nullptr);
|
||||
SNPRINTF_UTF8(actname, DATA_("%sAction"), owner_id->name + 2);
|
||||
}
|
||||
else if (GS(id->name) == ID_KE) {
|
||||
Key *key = reinterpret_cast<Key *>(id);
|
||||
SNPRINTF_UTF8(actname, DATA_("%sAction"), key->from->name + 2);
|
||||
}
|
||||
else {
|
||||
SNPRINTF_UTF8(actname, DATA_("%sAction"), id->name + 2);
|
||||
}
|
||||
|
||||
/* create action */
|
||||
action = BKE_action_add(bmain, actname);
|
||||
|
||||
/* Decrement the default-1 user count, as assigning it will increase it again. */
|
||||
BLI_assert(action->id.us == 1);
|
||||
id_us_min(&action->id);
|
||||
}
|
||||
|
||||
/* Assigning the Action should always work here. The only reason it wouldn't, is when a legacy
|
||||
* Action of the wrong ID type is assigned, but since in this branch of the code we're only
|
||||
* dealing with either new or layered Actions, this will never fail. */
|
||||
const bool ok = animrig::assign_action(action, {*id, *adt});
|
||||
BLI_assert_msg(ok, "Expecting Action assignment to work here");
|
||||
UNUSED_VARS_NDEBUG(ok);
|
||||
|
||||
/* Tag depsgraph to be rebuilt to include time dependency. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
|
||||
|
||||
/* return the action */
|
||||
return adt->action;
|
||||
}
|
||||
|
||||
void animdata_fcurve_delete(AnimData *adt, FCurve *fcu)
|
||||
{
|
||||
/* - If no AnimData, we've got nowhere to remove the F-Curve from
|
||||
* (this doesn't guarantee that the F-Curve is in there, but at least we tried).
|
||||
* - If no F-Curve, there is nothing to remove
|
||||
*/
|
||||
if (ELEM(nullptr, adt, fcu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_driver = fcu->driver != nullptr;
|
||||
if (is_driver) {
|
||||
BLI_remlink(&adt->drivers, fcu);
|
||||
}
|
||||
else if (adt->action) {
|
||||
Action &action = adt->action->wrap();
|
||||
action_fcurve_remove(action, *fcu);
|
||||
/* Return early to avoid the call to BKE_fcurve_free because the fcu has already been freed
|
||||
* by action_fcurve_remove. */
|
||||
return;
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
bool animdata_remove_empty_action(AnimData *adt)
|
||||
{
|
||||
if (adt->action != nullptr) {
|
||||
bAction *act = adt->action;
|
||||
DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH);
|
||||
Action &action = act->wrap();
|
||||
if (action.is_empty() && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
|
||||
id_us_min(&act->id);
|
||||
adt->action = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
const FCurve *fcurve_find_by_rna_path(const AnimData &adt,
|
||||
const StringRefNull rna_path,
|
||||
const int array_index)
|
||||
{
|
||||
BLI_assert(adt.action);
|
||||
if (!adt.action) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Action &action = adt.action->wrap();
|
||||
|
||||
const Slot *slot = action.slot_for_handle(adt.slot_handle);
|
||||
if (!slot) {
|
||||
/* No need to inspect anything if this ID does not have an Action Slot. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* No check for the slot's ID type. Not only do we not have the actual ID
|
||||
* to do this check, but also, since the Action and the slot have been
|
||||
* assigned, just trust that it's valid. */
|
||||
|
||||
/* Iterate the layers top-down, as higher-up animation overrides (or at least can override)
|
||||
* lower-down animation. */
|
||||
for (int layer_idx = action.layer_array_num - 1; layer_idx >= 0; layer_idx--) {
|
||||
const Layer *layer = action.layer(layer_idx);
|
||||
|
||||
/* TODO: refactor this into something nicer once we have different strip types. */
|
||||
for (const Strip *strip : layer->strips()) {
|
||||
switch (strip->type()) {
|
||||
case Strip::Type::Keyframe: {
|
||||
const StripKeyframeData &strip_data = strip->data<StripKeyframeData>(action);
|
||||
const Channelbag *channelbag_for_slot = strip_data.channelbag_for_slot(*slot);
|
||||
if (!channelbag_for_slot) {
|
||||
continue;
|
||||
}
|
||||
const FCurve *fcu = channelbag_for_slot->fcurve_find({rna_path, array_index});
|
||||
if (!fcu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This code assumes that there is only one strip, and that it's infinite. When that
|
||||
* changes, this code needs to be expanded to check for strip boundaries. */
|
||||
return fcu;
|
||||
}
|
||||
}
|
||||
/* Explicit lack of 'default' clause, to get compiler warnings when strip types are added. */
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Span<FCurve *> fcurves_for_assigned_action(AnimData *adt)
|
||||
{
|
||||
if (!adt || !adt->action) {
|
||||
return {};
|
||||
}
|
||||
return fcurves_for_action_slot(adt->action->wrap(), adt->slot_handle);
|
||||
}
|
||||
|
||||
Span<const FCurve *> fcurves_for_assigned_action(const AnimData *adt)
|
||||
{
|
||||
if (!adt || !adt->action) {
|
||||
return {};
|
||||
}
|
||||
return fcurves_for_action_slot(const_cast<const bAction *>(adt->action)->wrap(),
|
||||
adt->slot_handle);
|
||||
}
|
||||
|
||||
} // namespace blender::animrig
|
||||
Reference in New Issue
Block a user