tornavis/source/blender/editors/armature/armature_naming.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

610 lines
18 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edarmature
* Operators and API's for renaming bones both in and out of Edit Mode.
*
* This file contains functions/API's for renaming bones and/or working with them.
*/
#include <cstring>
#include "MEM_guardedalloc.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_string_utils.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_armature.hh"
#include "BKE_constraint.h"
#include "BKE_context.hh"
#include "BKE_deform.h"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_layer.h"
#include "BKE_main.h"
2023-11-14 09:30:40 +01:00
#include "BKE_modifier.hh"
#include "DEG_depsgraph.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_armature.hh"
#include "ED_screen.hh"
Anim: replace Bone Groups & Armature Layers with Bone Collections Armature layers (the 32 little dots) and bone groups are replaced with Bone Collections: - Bone collections are stored on the armature, and have a name that is unique within that armature. - An armature can have an arbitrary number of bone collections (instead of the fixed 32 layers). - Bones can be assigned to zero or more bone collections. - Bone collections have a visibility setting, just like objects in scene collections. - When a bone is in at least one collection, and all its collections in are hidden, the bone is hidden. In other cases (in any visible collection, or in no collection at all), the bone visibility is determined by its own 'hidden' flag. - For now, bone collections cannot be nested; they are a flat list just like bone groups were. Nestability of bone collections is intended to be implemented in a later 4.x release. - Since bone collections are defined on the armature, they can be used from both pose mode and edit mode. Versioning converts bone groups and armature layers to new bone collections. Layers that do not contain any bones are skipped. The old data structures remain in DNA and are unaltered, for limited forward compatibility. That way at least a save with Blender 4.0 will not immediately erase the bone group and armature layers and their bone assignments. Shortcuts: - M/Shift+M in pose/edit mode: move to collection (M) and add to collection (shift+M). This works similar to the M/Shift+M menus for objects & scene collections. - Ctrl+G in pose mode shows a port of the old 'bone groups' menu. This is likely to be removed in the near future, as the functionality overlaps with the M/Shift+M menus. This is the first commit of a series; the bone collections feature will be improved before the Blender 4.0 release. See #108941 for more info. Pull request: https://projects.blender.org/blender/blender/pulls/109976
2023-08-22 12:15:54 +02:00
#include "ANIM_bone_collections.h"
#include "armature_intern.h"
/* -------------------------------------------------------------------- */
/** \name Unique Bone Name Utility (Edit Mode)
* \{ */
/* NOTE: there's a ed_armature_bone_unique_name() too! */
static bool editbone_unique_check(void *arg, const char *name)
{
struct Arg {
ListBase *lb;
void *bone;
} *data = static_cast<Arg *>(arg);
EditBone *dupli = ED_armature_ebone_find_name(data->lb, name);
return dupli && dupli != data->bone;
}
void ED_armature_ebone_unique_name(ListBase *ebones, char *name, EditBone *bone)
{
struct {
ListBase *lb;
void *bone;
} data;
data.lb = ebones;
data.bone = bone;
BLI_uniquename_cb(editbone_unique_check, &data, DATA_("Bone"), '.', name, sizeof(bone->name));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Unique Bone Name Utility (Object Mode)
* \{ */
static bool bone_unique_check(void *arg, const char *name)
{
return BKE_armature_find_bone_name((bArmature *)arg, name) != nullptr;
}
static void ed_armature_bone_unique_name(bArmature *arm, char *name)
{
BLI_uniquename_cb(bone_unique_check, (void *)arm, DATA_("Bone"), '.', name, sizeof(Bone::name));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bone Renaming (Object & Edit Mode API)
* \{ */
/* helper call for armature_bone_rename */
static void constraint_bone_name_fix(Object *ob,
ListBase *conlist,
const char *oldname,
const char *newname)
{
LISTBASE_FOREACH (bConstraint *, curcon, conlist) {
ListBase targets = {nullptr, nullptr};
/* constraint targets */
if (BKE_constraint_targets_get(curcon, &targets)) {
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
if (ct->tar == ob) {
if (STREQ(ct->subtarget, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(ct->subtarget, newname);
}
}
}
BKE_constraint_targets_flush(curcon, &targets, false);
}
/* action constraints */
if (curcon->type == CONSTRAINT_TYPE_ACTION) {
bActionConstraint *actcon = (bActionConstraint *)curcon->data;
BKE_action_fix_paths_rename(
&ob->id, actcon->act, "pose.bones", oldname, newname, 0, 0, true);
}
}
}
void ED_armature_bone_rename(Main *bmain,
bArmature *arm,
const char *oldnamep,
const char *newnamep)
{
Object *ob;
char newname[MAXBONENAME];
char oldname[MAXBONENAME];
/* names better differ! */
if (!STREQLEN(oldnamep, newnamep, MAXBONENAME)) {
/* we alter newname string... so make copy */
2023-05-09 04:50:37 +02:00
STRNCPY(newname, newnamep);
/* we use oldname for search... so make copy */
2023-05-09 04:50:37 +02:00
STRNCPY(oldname, oldnamep);
/* now check if we're in editmode, we need to find the unique name */
if (arm->edbo) {
EditBone *eBone = ED_armature_ebone_find_name(arm->edbo, oldname);
if (eBone) {
ED_armature_ebone_unique_name(arm->edbo, newname, nullptr);
2023-05-09 04:50:37 +02:00
STRNCPY(eBone->name, newname);
}
else {
return;
}
}
else {
Bone *bone = BKE_armature_find_bone_name(arm, oldname);
if (bone) {
ed_armature_bone_unique_name(arm, newname);
if (arm->bonehash) {
BLI_assert(BLI_ghash_haskey(arm->bonehash, bone->name));
BLI_ghash_remove(arm->bonehash, bone->name, nullptr, nullptr);
}
2023-05-09 04:50:37 +02:00
STRNCPY(bone->name, newname);
if (arm->bonehash) {
BLI_ghash_insert(arm->bonehash, bone->name, bone);
}
}
else {
return;
}
}
/* force copy on write to update database */
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
/* do entire dbase - objects */
for (ob = static_cast<Object *>(bmain->objects.first); ob;
ob = static_cast<Object *>(ob->id.next)) {
/* we have the object using the armature */
if (arm == ob->data) {
Object *cob;
/* Rename the pose channel, if it exists */
if (ob->pose) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname);
if (pchan) {
GHash *gh = ob->pose->chanhash;
/* remove the old hash entry, and replace with the new name */
if (gh) {
BLI_assert(BLI_ghash_haskey(gh, pchan->name));
BLI_ghash_remove(gh, pchan->name, nullptr, nullptr);
}
2023-05-09 04:50:37 +02:00
STRNCPY(pchan->name, newname);
if (gh) {
BLI_ghash_insert(gh, pchan->name, pchan);
}
}
BLI_assert(BKE_pose_channels_is_valid(ob->pose) == true);
}
/* Update any object constraints to use the new bone name */
for (cob = static_cast<Object *>(bmain->objects.first); cob;
cob = static_cast<Object *>(cob->id.next))
{
2019-04-22 01:19:45 +02:00
if (cob->constraints.first) {
constraint_bone_name_fix(ob, &cob->constraints, oldname, newname);
2019-04-22 01:19:45 +02:00
}
if (cob->pose) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &cob->pose->chanbase) {
constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname);
}
}
}
}
/* See if an object is parented to this armature */
if (ob->parent && (ob->parent->data == arm)) {
if (ob->partype == PARBONE) {
/* bone name in object */
2019-04-22 01:19:45 +02:00
if (STREQ(ob->parsubstr, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(ob->parsubstr, newname);
2019-04-22 01:19:45 +02:00
}
}
}
if (BKE_modifiers_uses_armature(ob, arm) && BKE_object_supports_vertex_groups(ob)) {
bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname);
if (dg) {
2023-05-09 04:50:37 +02:00
STRNCPY(dg->name, newname);
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
}
}
/* fix modifiers that might be using this name */
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
switch (md->type) {
case eModifierType_Hook: {
HookModifierData *hmd = (HookModifierData *)md;
if (hmd->object && (hmd->object->data == arm)) {
2019-04-22 01:19:45 +02:00
if (STREQ(hmd->subtarget, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(hmd->subtarget, newname);
2019-04-22 01:19:45 +02:00
}
}
break;
}
case eModifierType_UVWarp: {
UVWarpModifierData *umd = (UVWarpModifierData *)md;
if (umd->object_src && (umd->object_src->data == arm)) {
2019-04-22 01:19:45 +02:00
if (STREQ(umd->bone_src, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(umd->bone_src, newname);
2019-04-22 01:19:45 +02:00
}
}
if (umd->object_dst && (umd->object_dst->data == arm)) {
2019-04-22 01:19:45 +02:00
if (STREQ(umd->bone_dst, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(umd->bone_dst, newname);
2019-04-22 01:19:45 +02:00
}
}
break;
}
default:
break;
}
}
/* fix camera focus */
if (ob->type == OB_CAMERA) {
Camera *cam = (Camera *)ob->data;
if ((cam->dof.focus_object != nullptr) && (cam->dof.focus_object->data == arm)) {
if (STREQ(cam->dof.focus_subtarget, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(cam->dof.focus_subtarget, newname);
DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE);
}
}
}
/* fix grease pencil modifiers and vertex groups */
if (ob->type == OB_GPENCIL_LEGACY) {
bGPdata *gpd = (bGPdata *)ob->data;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->parent != nullptr) && (gpl->parent->data == arm)) {
2019-04-22 01:19:45 +02:00
if (STREQ(gpl->parsubstr, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(gpl->parsubstr, newname);
2019-04-22 01:19:45 +02:00
}
}
}
LISTBASE_FOREACH (GpencilModifierData *, gp_md, &ob->greasepencil_modifiers) {
switch (gp_md->type) {
case eGpencilModifierType_Armature: {
ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)gp_md;
if (mmd->object && mmd->object->data == arm) {
bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname);
if (dg) {
2023-05-09 04:50:37 +02:00
STRNCPY(dg->name, newname);
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
}
}
break;
}
case eGpencilModifierType_Hook: {
HookGpencilModifierData *hgp_md = (HookGpencilModifierData *)gp_md;
if (hgp_md->object && (hgp_md->object->data == arm)) {
2019-04-22 01:19:45 +02:00
if (STREQ(hgp_md->subtarget, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(hgp_md->subtarget, newname);
2019-04-22 01:19:45 +02:00
}
}
break;
}
default:
break;
}
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
/* Fix all animdata that may refer to this bone -
* we can't just do the ones attached to objects,
* since other ID-blocks may have drivers referring to this bone #29822. */
/* XXX: the ID here is for armatures,
* but most bone drivers are actually on the object instead. */
{
2015-04-04 06:13:56 +02:00
BKE_animdata_fix_paths_rename_all(&arm->id, "pose.bones", oldname, newname);
}
/* correct view locking */
{
bScreen *screen;
for (screen = static_cast<bScreen *>(bmain->screens.first); screen;
screen = static_cast<bScreen *>(screen->id.next))
{
/* add regions */
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
if (v3d->ob_center && v3d->ob_center->data == arm) {
if (STREQ(v3d->ob_center_bone, oldname)) {
2023-05-09 04:50:37 +02:00
STRNCPY(v3d->ob_center_bone, newname);
}
}
}
}
}
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bone Flipping (Object & Edit Mode API)
* \{ */
struct BoneFlipNameData {
BoneFlipNameData *next, *prev;
char *name;
char name_flip[MAXBONENAME];
};
void ED_armature_bones_flip_names(Main *bmain,
bArmature *arm,
ListBase *bones_names,
const bool do_strip_numbers)
{
ListBase bones_names_conflicts = {nullptr};
BoneFlipNameData *bfn;
/* First pass: generate flip names, and blindly rename.
* If rename did not yield expected result,
* store both bone's name and expected flipped one into temp list for second pass. */
LISTBASE_FOREACH (LinkData *, link, bones_names) {
char name_flip[MAXBONENAME];
char *name = static_cast<char *>(link->data);
/* WARNING: if do_strip_numbers is set, expect completely mismatched names in cases like
* Bone.R, Bone.R.001, Bone.R.002, etc. */
BLI_string_flip_side_name(name_flip, name, do_strip_numbers, sizeof(name_flip));
ED_armature_bone_rename(bmain, arm, name, name_flip);
if (!STREQ(name, name_flip)) {
bfn = static_cast<BoneFlipNameData *>(alloca(sizeof(BoneFlipNameData)));
bfn->name = name;
2023-05-09 04:50:37 +02:00
STRNCPY(bfn->name_flip, name_flip);
BLI_addtail(&bones_names_conflicts, bfn);
}
}
/* Second pass to handle the bones that have naming conflicts with other bones.
* Note that if the other bone was not selected, its name was not flipped,
* so conflict remains and that second rename simply generates a new numbered alternative name.
*/
LISTBASE_FOREACH (BoneFlipNameData *, bfn, &bones_names_conflicts) {
ED_armature_bone_rename(bmain, arm, bfn->name, bfn->name_flip);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Flip Bone Names (Edit Mode Operator)
* \{ */
static int armature_flip_names_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_edit_object(C);
const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = static_cast<bArmature *>(ob->data);
/* Paranoia check. */
if (ob_active->pose == nullptr) {
continue;
}
ListBase bones_names = {nullptr};
LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
if (EBONE_VISIBLE(arm, ebone)) {
if (ebone->flag & BONE_SELECTED) {
BLI_addtail(&bones_names, BLI_genericNodeN(ebone->name));
if (arm->flag & ARM_MIRROR_EDIT) {
2018-10-07 08:49:22 +02:00
EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) {
BLI_addtail(&bones_names, BLI_genericNodeN(flipbone->name));
}
}
}
}
}
if (BLI_listbase_is_empty(&bones_names)) {
continue;
}
ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers);
BLI_freelistN(&bones_names);
/* since we renamed stuff... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
/* copied from #rna_Bone_update_renamed */
/* redraw view */
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
/* update animation channels */
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void ARMATURE_OT_flip_names(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Flip Names";
ot->idname = "ARMATURE_OT_flip_names";
ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones";
/* api callbacks */
ot->exec = armature_flip_names_exec;
ot->poll = ED_operator_editarmature;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"do_strip_numbers",
false,
"Strip Numbers",
"Try to remove right-most dot-number from flipped names.\n"
"Warning: May result in incoherent naming in some cases");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bone Auto Side Names (Edit Mode Operator)
* \{ */
static int armature_autoside_names_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
char newname[MAXBONENAME];
const short axis = RNA_enum_get(op->ptr, "type");
bool changed_multi = false;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = static_cast<bArmature *>(ob->data);
bool changed = false;
/* Paranoia checks. */
if (ELEM(nullptr, ob, ob->pose)) {
continue;
}
LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
if (EBONE_EDITABLE(ebone)) {
/* We first need to do the flipped bone, then the original one.
* Otherwise we can't find the flipped one because of the bone name change. */
if (arm->flag & ARM_MIRROR_EDIT) {
EditBone *flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) {
2023-05-09 04:50:37 +02:00
STRNCPY(newname, flipbone->name);
if (bone_autoside_name(newname, 1, axis, flipbone->head[axis], flipbone->tail[axis])) {
ED_armature_bone_rename(bmain, arm, flipbone->name, newname);
changed = true;
}
}
}
2023-05-09 04:50:37 +02:00
STRNCPY(newname, ebone->name);
if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis])) {
ED_armature_bone_rename(bmain, arm, ebone->name, newname);
changed = true;
}
}
}
if (!changed) {
continue;
}
changed_multi = true;
/* Since we renamed stuff... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
/* NOTE: notifier might evolve. */
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
}
MEM_freeN(objects);
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void ARMATURE_OT_autoside_names(wmOperatorType *ot)
{
static const EnumPropertyItem axis_items[] = {
{0, "XAXIS", 0, "X-Axis", "Left/Right"},
{1, "YAXIS", 0, "Y-Axis", "Front/Back"},
{2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Auto-Name by Axis";
ot->idname = "ARMATURE_OT_autoside_names";
ot->description =
"Automatically renames the selected bones according to which side of the target axis they "
"fall on";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = armature_autoside_names_exec;
ot->poll = ED_operator_editarmature;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* settings */
UI: fix and improve a few messages - "Rename Channels": only one channel can be renamed at a time, use singular. - "Copy Markers to Scene": rephrase erroneous operator description. - "Axis tag names with": grammar. - "Close or open the selected stroke adding an edge from last to first point": "segment" is preferred to "edge" elsewhere in the context of curves or Grease Pencil. - "Number of subdivisions [points] by edge[s]": replace "by edge" with "per segment" for the same reason. - "Compatibility mode for SL, OpenSim...": expand to explain that SL and OpenSim are respectively Second Life and OpenSimulator. - "W/m^2" -> "W/m²". This symbol is widely supported and should be preferred for exponentiation outside of code. - "Effect on tracks which are tracked less than specified amount of frames", "Effect on tracks which have a larger reprojection error": Use "Affect" and "number" instead of "amount". - "Hull curve" -> "Envelope". This is a calque from German Hüllkurve meaning envelope. - "Frquency Cutoff" -> "Frequency Cutoff" (typo) - "Check if Select Left or Right": rephrase to "Based on Mouse Position" as it better explains the action of the operator. - "Make cut event if strip is not selected ..." -> even (typo) - "Shear selected items along the horizontal screen axis": Rephrase as this transform operator can act in many different axes. - Tonemapping compositing node: the two algorithms "R/D Photoreceptor" and "Rh Simple" only had names, but no description. Add ones explaining at least the basic principle and where the names come from. - In the "Matte dilate/erode side" description for the Keying node's Dilate/Erode socket, "side" was likely a typo for "size". Reformulate the description to make it clearer, inspired by similar ones. - "Width of the blur edge" -> "Width of the blur for the transition"; "Edge angle" -> "Angle of the transition"; "Wipe direction" -> "Whether to fade in or out": Better explains the sequencer wipe transition (inspired by the manual). - OSL shaders now supported on some GPU backends. - "Add a new repeat input and output nodes " -> "Add new" (typo). Pull Request: https://projects.blender.org/blender/blender/pulls/110321
2023-07-24 21:22:07 +02:00
ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis to tag names with");
}
/** \} */