Add Chromium-only Blender WebEngine parity work
This commit is contained in:
59
blender-5.2.0/source/blender/editors/armature/CMakeLists.txt
Normal file
59
blender-5.2.0/source/blender/editors/armature/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../makesrna
|
||||
../../../../intern/eigen
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
armature_add.cc
|
||||
armature_edit.cc
|
||||
armature_naming.cc
|
||||
armature_ops.cc
|
||||
armature_relations.cc
|
||||
armature_select.cc
|
||||
armature_skinning.cc
|
||||
armature_utils.cc
|
||||
bone_collections.cc
|
||||
editarmature_undo.cc
|
||||
meshlaplacian.cc
|
||||
pose_edit.cc
|
||||
pose_lib_2.cc
|
||||
pose_select.cc
|
||||
pose_slide.cc
|
||||
pose_transform.cc
|
||||
pose_utils.cc
|
||||
|
||||
armature_intern.hh
|
||||
meshlaplacian.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::animrig
|
||||
PRIVATE bf::asset_system
|
||||
PRIVATE bf::blenfont
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
|
||||
blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_editor_armature bf_rna)
|
||||
2206
blender-5.2.0/source/blender/editors/armature/armature_add.cc
Normal file
2206
blender-5.2.0/source/blender/editors/armature/armature_add.cc
Normal file
File diff suppressed because it is too large
Load Diff
1627
blender-5.2.0/source/blender/editors/armature/armature_edit.cc
Normal file
1627
blender-5.2.0/source/blender/editors/armature/armature_edit.cc
Normal file
File diff suppressed because it is too large
Load Diff
328
blender-5.2.0/source/blender/editors/armature/armature_intern.hh
Normal file
328
blender-5.2.0/source/blender/editors/armature/armature_intern.hh
Normal file
@@ -0,0 +1,328 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "ED_anim_transformable.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Base;
|
||||
struct Bone;
|
||||
struct EditBone;
|
||||
struct GPUSelectResult;
|
||||
struct IDProperty;
|
||||
struct LinkData;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct bArmature;
|
||||
struct bContext;
|
||||
struct bPoseChannel;
|
||||
struct wmOperatorType;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Armature EditMode Operators
|
||||
* \{ */
|
||||
|
||||
void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_align(wmOperatorType *ot);
|
||||
void ARMATURE_OT_calculate_roll(wmOperatorType *ot);
|
||||
void ARMATURE_OT_roll_clear(wmOperatorType *ot);
|
||||
void ARMATURE_OT_switch_direction(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_subdivide(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_parent_set(wmOperatorType *ot);
|
||||
void ARMATURE_OT_parent_clear(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_select_all(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_mirror(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_more(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_less(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_hierarchy(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_linked_pick(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_linked(wmOperatorType *ot);
|
||||
void ARMATURE_OT_select_similar(wmOperatorType *ot);
|
||||
void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_delete(wmOperatorType *ot);
|
||||
void ARMATURE_OT_dissolve(wmOperatorType *ot);
|
||||
void ARMATURE_OT_duplicate(wmOperatorType *ot);
|
||||
void ARMATURE_OT_duplicate_rename(wmOperatorType *ot);
|
||||
void ARMATURE_OT_symmetrize(wmOperatorType *ot);
|
||||
void ARMATURE_OT_extrude(wmOperatorType *ot);
|
||||
void ARMATURE_OT_hide(wmOperatorType *ot);
|
||||
void ARMATURE_OT_reveal(wmOperatorType *ot);
|
||||
void ARMATURE_OT_click_extrude(wmOperatorType *ot);
|
||||
void ARMATURE_OT_fill(wmOperatorType *ot);
|
||||
void ARMATURE_OT_separate(wmOperatorType *ot);
|
||||
void ARMATURE_OT_split(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_autoside_names(wmOperatorType *ot);
|
||||
void ARMATURE_OT_flip_names(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_collection_add(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_remove(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_move(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_assign(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_create_and_assign(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_unassign(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_unassign_named(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_select(wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_deselect(wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_move_to_collection(wmOperatorType *ot);
|
||||
void ARMATURE_OT_assign_to_collection(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pose-Mode Operators
|
||||
* \{ */
|
||||
|
||||
void POSE_OT_hide(wmOperatorType *ot);
|
||||
void POSE_OT_reveal(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_armature_apply(wmOperatorType *ot);
|
||||
void POSE_OT_visual_transform_apply(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_rot_clear(wmOperatorType *ot);
|
||||
void POSE_OT_loc_clear(wmOperatorType *ot);
|
||||
void POSE_OT_scale_clear(wmOperatorType *ot);
|
||||
void POSE_OT_transforms_clear(wmOperatorType *ot);
|
||||
void POSE_OT_user_transforms_clear(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_copy(wmOperatorType *ot);
|
||||
void POSE_OT_paste(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_select_all(wmOperatorType *ot);
|
||||
void POSE_OT_select_parent(wmOperatorType *ot);
|
||||
void POSE_OT_select_hierarchy(wmOperatorType *ot);
|
||||
void POSE_OT_select_linked(wmOperatorType *ot);
|
||||
void POSE_OT_select_linked_pick(wmOperatorType *ot);
|
||||
void POSE_OT_select_constraint_target(wmOperatorType *ot);
|
||||
void POSE_OT_select_grouped(wmOperatorType *ot);
|
||||
void POSE_OT_select_mirror(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_paths_calculate(wmOperatorType *ot);
|
||||
void POSE_OT_paths_update(wmOperatorType *ot);
|
||||
void POSE_OT_paths_clear(wmOperatorType *ot);
|
||||
void POSE_OT_paths_range_update(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_autoside_names(wmOperatorType *ot);
|
||||
void POSE_OT_flip_names(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_rotation_mode_set(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_quaternions_flip(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pose Tool Utilities (for PoseLib, Pose Sliding, etc.)
|
||||
* \{ */
|
||||
|
||||
/* `pose_utils.cc` */
|
||||
|
||||
/**
|
||||
* Types of transforms to apply to a tPchanFCurveLink.
|
||||
*/
|
||||
enum eAction_TransformFlags {
|
||||
ACT_TRANS_LOC = (1 << 0),
|
||||
ACT_TRANS_ROT = (1 << 1),
|
||||
ACT_TRANS_SCALE = (1 << 2),
|
||||
|
||||
/* BBone shape - for all the parameters, provided one is set. */
|
||||
ACT_TRANS_BBONE = (1 << 3),
|
||||
ACT_TRANS_PROP = (1 << 4),
|
||||
|
||||
ACT_TRANS_ONLY = (ACT_TRANS_LOC | ACT_TRANS_ROT | ACT_TRANS_SCALE),
|
||||
ACT_TRANS_ALL = (ACT_TRANS_ONLY | ACT_TRANS_PROP),
|
||||
};
|
||||
|
||||
/* Stores values of an RNA property for use at a later date. */
|
||||
struct PropertySnapshot {
|
||||
PropertyRNA *property;
|
||||
/* Non-float properties are also stored as float. The length of the array matches the length of
|
||||
* the property. */
|
||||
Array<float> values;
|
||||
};
|
||||
|
||||
/* Temporary struct wrapping data used for pose sliding. */
|
||||
struct SlideSubject {
|
||||
SlideSubject *next, *prev;
|
||||
|
||||
/** F-Curves for this PoseChannel (wrapped with LinkData) */
|
||||
/** The AnimTransformable which the data is attached to */
|
||||
ed::AnimTransformable *transformable;
|
||||
/* A pointer to the data represented by this link. */
|
||||
PointerRNA ptr;
|
||||
/** F-Curves for this AnimTransformable. */
|
||||
Vector<FCurve *> fcurves;
|
||||
/* This is used as an optimization to only do blending on transform types that actually have
|
||||
* animation. */
|
||||
eAction_TransformFlags transform_flag;
|
||||
|
||||
/** Transform values at start of operator (to be restored before each modal step). */
|
||||
ed::TransformFloats old_loc;
|
||||
ed::Rotation old_rot;
|
||||
ed::TransformFloats old_scale;
|
||||
|
||||
/* Additional properties of the transformable to affect which are not custom properties. Bones
|
||||
* use this to store bbone data, e.g. `bbone_rollin`. */
|
||||
Vector<PropertySnapshot> additional_properties;
|
||||
|
||||
/* Custom properties defined via the UI. See ID::properties. */
|
||||
Vector<PropertySnapshot> properties;
|
||||
/* User defined properties through addons. See ID::system_properties. */
|
||||
Vector<PropertySnapshot> system_properties;
|
||||
};
|
||||
|
||||
/* ----------- */
|
||||
|
||||
/**
|
||||
* Build up a list of SlideSubject. The items put into the list depend on the mode of
|
||||
* the context.
|
||||
*/
|
||||
void slide_subjects_get(bContext *C, ListBaseT<SlideSubject> *slide_subjects);
|
||||
/** Free all slide subjects. */
|
||||
void slide_subjects_free(ListBaseT<SlideSubject> *slide_subjects);
|
||||
|
||||
/**
|
||||
* Helper for apply() / reset() - refresh the data.
|
||||
*/
|
||||
void slide_subjects_refresh(bContext *C, const SlideSubject &slide_subject);
|
||||
/**
|
||||
* Reset changes made to current slide subjects back to their stored values.
|
||||
*/
|
||||
void slide_subjects_reset(ListBaseT<SlideSubject> *slide_subjects);
|
||||
/** Perform auto-key-framing after changes were made + confirmed. */
|
||||
void slide_subjects_autokey(bContext *C,
|
||||
Scene *scene,
|
||||
const ListBaseT<SlideSubject> *slide_subjects);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name PoseLib
|
||||
* \{ */
|
||||
|
||||
/* `pose_lib_2.cc` */
|
||||
|
||||
void POSELIB_OT_apply_pose_asset(wmOperatorType *ot);
|
||||
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pose Sliding Tools
|
||||
* \{ */
|
||||
|
||||
/* `pose_slide.cc` */
|
||||
|
||||
void POSE_OT_push(wmOperatorType *ot);
|
||||
void POSE_OT_relax(wmOperatorType *ot);
|
||||
void POSE_OT_blend_with_rest(wmOperatorType *ot);
|
||||
void POSE_OT_breakdown(wmOperatorType *ot);
|
||||
void POSE_OT_blend_to_neighbors(wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_propagate(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Various Armature Edit/Pose Editing API's
|
||||
* \{ */
|
||||
|
||||
/* Ideally, many of these defines would not be needed as everything would be strictly
|
||||
* self-contained within each file,
|
||||
* but some tools still have a bit of overlap which makes things messy -- Feb 2013
|
||||
*/
|
||||
|
||||
EditBone *make_boneList(ListBaseT<EditBone> *edbo, ListBaseT<Bone> *bones, Bone *actBone);
|
||||
|
||||
/* Duplicate method. */
|
||||
|
||||
EditBone *duplicateEditBone(EditBone *cur_bone,
|
||||
const char *name,
|
||||
ListBaseT<EditBone> *editbones,
|
||||
Object *ob);
|
||||
|
||||
/* Duplicate method (cross objects). */
|
||||
|
||||
/**
|
||||
* \param editbones: The target list.
|
||||
*/
|
||||
EditBone *duplicateEditBoneObjects(EditBone *cur_bone,
|
||||
const char *name,
|
||||
ListBaseT<EditBone> *editbones,
|
||||
Object *src_ob,
|
||||
Object *dst_ob);
|
||||
|
||||
/** Adds an EditBone between the nominated locations (should be in the right space). */
|
||||
EditBone *add_points_bone(Object *obedit, float head[3], float tail[3]);
|
||||
void bone_free(bArmature *arm, EditBone *bone);
|
||||
|
||||
void armature_tag_select_mirrored(bArmature *arm);
|
||||
/**
|
||||
* Helper function for tools to work on mirrored parts.
|
||||
* it leaves mirrored bones selected then too, which is a good indication of what happened.
|
||||
*/
|
||||
void armature_select_mirrored_ex(bArmature *arm, eBone_Flag flag);
|
||||
void armature_select_mirrored(bArmature *arm);
|
||||
/** Only works when tagged. */
|
||||
void armature_tag_unselect(bArmature *arm);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Selection Picking
|
||||
* \{ */
|
||||
|
||||
EditBone *ED_armature_pick_ebone_from_selectbuffer(Span<Base *> bases,
|
||||
const GPUSelectResult *hit_results,
|
||||
int hits,
|
||||
bool findunsel,
|
||||
bool do_nearest,
|
||||
Base **r_base);
|
||||
bPoseChannel *ED_armature_pick_pchan_from_selectbuffer(Span<Base *> bases,
|
||||
const GPUSelectResult *hit_results,
|
||||
int hits,
|
||||
bool findunsel,
|
||||
bool do_nearest,
|
||||
Base **r_base);
|
||||
Bone *ED_armature_pick_bone_from_selectbuffer(Span<Base *> bases,
|
||||
const GPUSelectResult *hit_results,
|
||||
int hits,
|
||||
bool findunsel,
|
||||
bool do_nearest,
|
||||
Base **r_base);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Iteration
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* XXX: bone_looper is only to be used when we want to access settings
|
||||
* (i.e. editability/visibility/selected) that context doesn't offer.
|
||||
*/
|
||||
int bone_looper(Object *ob, Bone *bone, void *data, int (*bone_func)(Object *, Bone *, void *));
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
629
blender-5.2.0/source/blender/editors/armature/armature_naming.cc
Normal file
629
blender-5.2.0/source/blender/editors/armature/armature_naming.cc
Normal file
@@ -0,0 +1,629 @@
|
||||
/* 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 "DNA_armature_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_deform.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_main.hh"
|
||||
#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"
|
||||
|
||||
#include "ANIM_armature.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Unique Bone Name Utility (Edit Mode)
|
||||
* \{ */
|
||||
|
||||
/* NOTE: there's a ed_armature_bone_unique_name() too! */
|
||||
static bool editbone_unique_check(ListBaseT<EditBone> *ebones,
|
||||
const StringRefNull name,
|
||||
EditBone *bone)
|
||||
{
|
||||
if (bone) {
|
||||
/* This indicates that there is a bone to ignore. This means ED_armature_ebone_find_name()
|
||||
* cannot be used, as it might return the bone we should be ignoring. */
|
||||
for (EditBone *ebone : ListBaseWrapper<EditBone>(ebones)) {
|
||||
if (ebone->name == name && ebone != bone) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EditBone *dupli = ED_armature_ebone_find_name(ebones, name.c_str());
|
||||
return dupli && dupli != bone;
|
||||
}
|
||||
|
||||
void ED_armature_ebone_unique_name(ListBaseT<EditBone> *ebones, char *name, EditBone *bone)
|
||||
{
|
||||
BLI_uniquename_cb(
|
||||
[&](const StringRefNull check_name) {
|
||||
return editbone_unique_check(ebones, check_name, bone);
|
||||
},
|
||||
DATA_(animrig::bone_default_name),
|
||||
'.',
|
||||
name,
|
||||
sizeof(bone->name));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Unique Bone Name Utility (Object Mode)
|
||||
* \{ */
|
||||
|
||||
static void ed_armature_bone_unique_name(bArmature *arm, char *name)
|
||||
{
|
||||
BLI_uniquename_cb(
|
||||
[&](const StringRefNull check_name) {
|
||||
return BKE_armature_find_bone_name(arm, check_name.c_str()) != nullptr;
|
||||
},
|
||||
DATA_(animrig::bone_default_name),
|
||||
'.',
|
||||
name,
|
||||
sizeof(Bone::name));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Bone Renaming (Object & Edit Mode API)
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Helper call for `armature_bone_rename()`.
|
||||
*
|
||||
* \param rename_ob: The object whose bone was renamed.
|
||||
* \param constraint_ob: The object that owns the constraints in `conlist`.
|
||||
*/
|
||||
static void constraint_bone_name_fix(Object *rename_ob,
|
||||
Object *constraint_ob,
|
||||
ListBaseT<bConstraint> *conlist,
|
||||
const char *oldname,
|
||||
const char *newname)
|
||||
{
|
||||
for (bConstraint &curcon : *conlist) {
|
||||
ListBaseT<bConstraintTarget> targets = {nullptr, nullptr};
|
||||
|
||||
/* constraint targets */
|
||||
if (BKE_constraint_targets_get(&curcon, &targets)) {
|
||||
for (bConstraintTarget &ct : targets) {
|
||||
if (ct.tar == rename_ob) {
|
||||
if (STREQ(ct.subtarget, oldname)) {
|
||||
STRNCPY_UTF8(ct.subtarget, newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_constraint_targets_flush(&curcon, &targets, false);
|
||||
}
|
||||
|
||||
/* Actions from action constraints.
|
||||
*
|
||||
* We only rename channels in the action if the action constraint and the
|
||||
* bone rename are from the same object. This is because the action of an
|
||||
* action constraint animates the constrained object/bone, it does not
|
||||
* animate the constraint target. */
|
||||
if (curcon.type == CONSTRAINT_TYPE_ACTION && constraint_ob == rename_ob) {
|
||||
bActionConstraint *actcon = static_cast<bActionConstraint *>(curcon.data);
|
||||
BKE_action_fix_paths_rename(&rename_ob->id,
|
||||
actcon->act,
|
||||
actcon->action_slot_handle,
|
||||
"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 */
|
||||
STRNCPY_UTF8(newname, newnamep);
|
||||
/* we use oldname for search... so make copy */
|
||||
STRNCPY(oldname, oldnamep); /* Allow non UTF8 encoding for the old name. */
|
||||
|
||||
/* 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);
|
||||
STRNCPY_UTF8(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);
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(bone->name, newname);
|
||||
|
||||
if (arm->bonehash) {
|
||||
BLI_ghash_insert(arm->bonehash, bone->name, bone);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* force evaluation copy to update database */
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
|
||||
/* 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 (id_cast<const ID *>(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);
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(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))
|
||||
{
|
||||
if (cob->constraints.first) {
|
||||
constraint_bone_name_fix(ob, cob, &cob->constraints, oldname, newname);
|
||||
}
|
||||
if (cob->pose) {
|
||||
for (bPoseChannel &pchan : cob->pose->chanbase) {
|
||||
constraint_bone_name_fix(ob, cob, &pchan.constraints, oldname, newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* See if an object is parented to this armature */
|
||||
if (ob->parent && (ob->parent->data == id_cast<const ID *>(arm))) {
|
||||
if (ob->partype == PARBONE) {
|
||||
/* bone name in object */
|
||||
if (STREQ(ob->parsubstr, oldname)) {
|
||||
STRNCPY_UTF8(ob->parsubstr, newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BKE_modifiers_uses_armature(ob, arm) && BKE_object_supports_vertex_groups(ob)) {
|
||||
if (BKE_object_defgroup_find_name(ob, newname)) {
|
||||
WM_global_reportf(eReportType::RPT_WARNING,
|
||||
"New bone name collides with an existing vertex "
|
||||
"group name, vertex group "
|
||||
"names are unchanged. (%s::%s)",
|
||||
&ob->id.name[2],
|
||||
newname);
|
||||
/* Not renaming vertex group could cause bone to bind to other vertex group, in this case
|
||||
* deformation could change, so we tag this object for depsgraph update. */
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
else if (bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname)) {
|
||||
STRNCPY_UTF8(dg->name, newname);
|
||||
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
/* Update vgroup names stored in CurvesGeometry */
|
||||
BKE_grease_pencil_vgroup_name_update(ob, oldname, dg->name);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
/* fix modifiers that might be using this name */
|
||||
for (ModifierData &md : ob->modifiers) {
|
||||
switch (md.type) {
|
||||
case eModifierType_Hook: {
|
||||
HookModifierData *hmd = reinterpret_cast<HookModifierData *>(&md);
|
||||
|
||||
if (hmd->object && (hmd->object->data == id_cast<const ID *>(arm))) {
|
||||
if (STREQ(hmd->subtarget, oldname)) {
|
||||
STRNCPY_UTF8(hmd->subtarget, newname);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eModifierType_UVWarp: {
|
||||
UVWarpModifierData *umd = reinterpret_cast<UVWarpModifierData *>(&md);
|
||||
|
||||
if (umd->object_src && (umd->object_src->data == id_cast<const ID *>(arm))) {
|
||||
if (STREQ(umd->bone_src, oldname)) {
|
||||
STRNCPY_UTF8(umd->bone_src, newname);
|
||||
}
|
||||
}
|
||||
if (umd->object_dst && (umd->object_dst->data == id_cast<const ID *>(arm))) {
|
||||
if (STREQ(umd->bone_dst, oldname)) {
|
||||
STRNCPY_UTF8(umd->bone_dst, newname);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fix camera focus */
|
||||
if (ob->type == OB_CAMERA) {
|
||||
Camera *cam = id_cast<Camera *>(ob->data);
|
||||
if ((cam->dof.focus_object != nullptr) &&
|
||||
(cam->dof.focus_object->data == id_cast<const ID *>(arm)))
|
||||
{
|
||||
if (STREQ(cam->dof.focus_subtarget, oldname)) {
|
||||
STRNCPY_UTF8(cam->dof.focus_subtarget, newname);
|
||||
DEG_id_tag_update(&cam->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
GreasePencil &grease_pencil = *id_cast<GreasePencil *>(ob->data);
|
||||
for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
|
||||
Object *parent = layer->parent;
|
||||
if (parent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
StringRefNull bone_name = layer->parent_bone_name();
|
||||
if (!bone_name.is_empty() && bone_name == StringRef(oldname)) {
|
||||
layer->set_parent_bone_name(newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
{
|
||||
BKE_animdata_fix_paths_rename_all(&arm->id, "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 */
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
for (SpaceLink &sl : area.spacedata) {
|
||||
if (sl.spacetype == SPACE_VIEW3D) {
|
||||
View3D *v3d = reinterpret_cast<View3D *>(&sl);
|
||||
if (v3d->ob_center && v3d->ob_center->data == id_cast<const ID *>(arm)) {
|
||||
if (STREQ(v3d->ob_center_bone, oldname)) {
|
||||
STRNCPY_UTF8(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,
|
||||
ListBaseT<LinkData> *bones_names,
|
||||
const bool do_strip_numbers)
|
||||
{
|
||||
ListBaseT<BoneFlipNameData> 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. */
|
||||
for (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;
|
||||
STRNCPY_UTF8(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.
|
||||
*/
|
||||
for (BoneFlipNameData &bfn : bones_names_conflicts) {
|
||||
ED_armature_bone_rename(bmain, arm, bfn.name, bfn.name_flip);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Flip Bone Names (Edit Mode Operator)
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus 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");
|
||||
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
|
||||
*bmain, scene, view_layer, CTX_wm_view3d(C));
|
||||
for (Object *ob : objects) {
|
||||
bArmature *arm = id_cast<bArmature *>(ob->data);
|
||||
|
||||
/* Paranoia check. */
|
||||
if (ob_active->pose == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ListBaseT<LinkData> bones_names = {nullptr};
|
||||
|
||||
for (EditBone &ebone : *arm->edbo) {
|
||||
if (animrig::bone_is_selected(arm, &ebone)) {
|
||||
BLI_addtail(&bones_names, BLI_genericNodeN(ebone.name));
|
||||
|
||||
if (arm->flag & ARM_MIRROR_EDIT) {
|
||||
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 (bones_names.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers);
|
||||
|
||||
bones_names.free_no_destruct();
|
||||
|
||||
/* since we renamed stuff... */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
|
||||
/* copied from #rna_Bone_update_renamed */
|
||||
/* Redraw Outliner / Dope-sheet. */
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_RENAME, ob->data);
|
||||
|
||||
/* update animation channels */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data);
|
||||
}
|
||||
|
||||
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 wmOperatorStatus 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;
|
||||
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
|
||||
*bmain, scene, view_layer, CTX_wm_view3d(C));
|
||||
for (Object *ob : objects) {
|
||||
bArmature *arm = id_cast<bArmature *>(ob->data);
|
||||
bool changed = false;
|
||||
|
||||
/* Paranoia checks. */
|
||||
if (ELEM(nullptr, ob, ob->pose)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (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)) {
|
||||
STRNCPY_UTF8(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(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);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_RENAME, ob->data);
|
||||
|
||||
/* Update animation channels */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data);
|
||||
|
||||
/* NOTE: notifier might evolve. */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
}
|
||||
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 */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis to tag names with");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
181
blender-5.2.0/source/blender/editors/armature/armature_ops.cc
Normal file
181
blender-5.2.0/source/blender/editors/armature/armature_ops.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
*/
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ************************** registration **********************************/
|
||||
|
||||
void ED_operatortypes_armature()
|
||||
{
|
||||
/* Both operators `ARMATURE_OT_*` and `POSE_OT_*` are registered here. */
|
||||
|
||||
/* EDIT ARMATURE */
|
||||
WM_operatortype_append(ARMATURE_OT_bone_primitive_add);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_align);
|
||||
WM_operatortype_append(ARMATURE_OT_calculate_roll);
|
||||
WM_operatortype_append(ARMATURE_OT_roll_clear);
|
||||
WM_operatortype_append(ARMATURE_OT_switch_direction);
|
||||
WM_operatortype_append(ARMATURE_OT_subdivide);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_parent_set);
|
||||
WM_operatortype_append(ARMATURE_OT_parent_clear);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_select_all);
|
||||
WM_operatortype_append(ARMATURE_OT_select_mirror);
|
||||
WM_operatortype_append(ARMATURE_OT_select_more);
|
||||
WM_operatortype_append(ARMATURE_OT_select_less);
|
||||
WM_operatortype_append(ARMATURE_OT_select_hierarchy);
|
||||
WM_operatortype_append(ARMATURE_OT_select_linked);
|
||||
WM_operatortype_append(ARMATURE_OT_select_linked_pick);
|
||||
WM_operatortype_append(ARMATURE_OT_select_similar);
|
||||
WM_operatortype_append(ARMATURE_OT_shortest_path_pick);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_delete);
|
||||
WM_operatortype_append(ARMATURE_OT_dissolve);
|
||||
WM_operatortype_append(ARMATURE_OT_duplicate);
|
||||
WM_operatortype_append(ARMATURE_OT_duplicate_rename);
|
||||
WM_operatortype_append(ARMATURE_OT_symmetrize);
|
||||
WM_operatortype_append(ARMATURE_OT_extrude);
|
||||
WM_operatortype_append(ARMATURE_OT_hide);
|
||||
WM_operatortype_append(ARMATURE_OT_reveal);
|
||||
WM_operatortype_append(ARMATURE_OT_click_extrude);
|
||||
WM_operatortype_append(ARMATURE_OT_fill);
|
||||
WM_operatortype_append(ARMATURE_OT_separate);
|
||||
WM_operatortype_append(ARMATURE_OT_split);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_autoside_names);
|
||||
WM_operatortype_append(ARMATURE_OT_flip_names);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_collection_add);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_remove);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_move);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_assign);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_create_and_assign);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_unassign);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_unassign_named);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_select);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_deselect);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_move_to_collection);
|
||||
WM_operatortype_append(ARMATURE_OT_assign_to_collection);
|
||||
|
||||
/* POSE */
|
||||
WM_operatortype_append(POSE_OT_hide);
|
||||
WM_operatortype_append(POSE_OT_reveal);
|
||||
|
||||
WM_operatortype_append(POSE_OT_armature_apply);
|
||||
WM_operatortype_append(POSE_OT_visual_transform_apply);
|
||||
|
||||
WM_operatortype_append(POSE_OT_rot_clear);
|
||||
WM_operatortype_append(POSE_OT_loc_clear);
|
||||
WM_operatortype_append(POSE_OT_scale_clear);
|
||||
WM_operatortype_append(POSE_OT_transforms_clear);
|
||||
WM_operatortype_append(POSE_OT_user_transforms_clear);
|
||||
|
||||
WM_operatortype_append(POSE_OT_copy);
|
||||
WM_operatortype_append(POSE_OT_paste);
|
||||
|
||||
WM_operatortype_append(POSE_OT_select_all);
|
||||
|
||||
WM_operatortype_append(POSE_OT_select_parent);
|
||||
WM_operatortype_append(POSE_OT_select_hierarchy);
|
||||
WM_operatortype_append(POSE_OT_select_linked);
|
||||
WM_operatortype_append(POSE_OT_select_linked_pick);
|
||||
WM_operatortype_append(POSE_OT_select_constraint_target);
|
||||
WM_operatortype_append(POSE_OT_select_grouped);
|
||||
WM_operatortype_append(POSE_OT_select_mirror);
|
||||
|
||||
WM_operatortype_append(POSE_OT_paths_calculate);
|
||||
WM_operatortype_append(POSE_OT_paths_update);
|
||||
WM_operatortype_append(POSE_OT_paths_clear);
|
||||
WM_operatortype_append(POSE_OT_paths_range_update);
|
||||
|
||||
WM_operatortype_append(POSE_OT_autoside_names);
|
||||
WM_operatortype_append(POSE_OT_flip_names);
|
||||
|
||||
WM_operatortype_append(POSE_OT_rotation_mode_set);
|
||||
|
||||
WM_operatortype_append(POSE_OT_quaternions_flip);
|
||||
|
||||
WM_operatortype_append(POSE_OT_propagate);
|
||||
|
||||
/* POSELIB */
|
||||
WM_operatortype_append(POSELIB_OT_apply_pose_asset);
|
||||
WM_operatortype_append(POSELIB_OT_blend_pose_asset);
|
||||
|
||||
/* POSE SLIDING */
|
||||
WM_operatortype_append(POSE_OT_push);
|
||||
WM_operatortype_append(POSE_OT_relax);
|
||||
WM_operatortype_append(POSE_OT_blend_with_rest);
|
||||
WM_operatortype_append(POSE_OT_breakdown);
|
||||
WM_operatortype_append(POSE_OT_blend_to_neighbors);
|
||||
}
|
||||
|
||||
void ED_operatormacros_armature()
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
wmOperatorTypeMacro *otmacro;
|
||||
|
||||
ot = WM_operatortype_append_macro(
|
||||
"ARMATURE_OT_duplicate_move",
|
||||
"Duplicate",
|
||||
"Make copies of the selected bones within the same armature and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
WM_operatortype_macro_define(ot, "ARMATURE_OT_duplicate");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
|
||||
ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_move",
|
||||
"Extrude",
|
||||
"Create new bones from the selected joints and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude");
|
||||
RNA_boolean_set(otmacro->ptr, "forked", false);
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
|
||||
/* XXX would it be nicer to just be able to have standard extrude_move,
|
||||
* but set the forked property separate?
|
||||
* that would require fixing a properties bug #19733. */
|
||||
ot = WM_operatortype_append_macro("ARMATURE_OT_extrude_forked",
|
||||
"Extrude Forked",
|
||||
"Create new bones from the selected joints and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
otmacro = WM_operatortype_macro_define(ot, "ARMATURE_OT_extrude");
|
||||
RNA_boolean_set(otmacro->ptr, "forked", true);
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
}
|
||||
|
||||
void ED_keymap_armature(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap;
|
||||
|
||||
/* Armature ------------------------ */
|
||||
/* only set in editmode armature, by space_view3d listener */
|
||||
keymap = WM_keymap_ensure(keyconf, "Armature", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
keymap->poll = ED_operator_editarmature;
|
||||
|
||||
/* Pose ------------------------ */
|
||||
/* only set in posemode, by space_view3d listener */
|
||||
keymap = WM_keymap_ensure(keyconf, "Pose", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
keymap->poll = ED_operator_posemode;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1222
blender-5.2.0/source/blender/editors/armature/armature_relations.cc
Normal file
1222
blender-5.2.0/source/blender/editors/armature/armature_relations.cc
Normal file
File diff suppressed because it is too large
Load Diff
2385
blender-5.2.0/source/blender/editors/armature/armature_select.cc
Normal file
2385
blender-5.2.0/source/blender/editors/armature/armature_select.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,514 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
* API's for creating vertex groups from bones
|
||||
* - Interfaces with heat weighting in meshlaplacian.
|
||||
*/
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_deform.hh"
|
||||
#include "BKE_mesh_iterators.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_deform.h"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_subdiv_mesh.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_mesh.hh"
|
||||
#include "ED_object_vgroup.hh"
|
||||
|
||||
#include "ANIM_bone_collections.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
#include "meshlaplacian.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ******************************* Bone Skinning *********************************************** */
|
||||
|
||||
static int bone_skinnable_cb(Object * /*ob*/, Bone *bone, void *datap)
|
||||
{
|
||||
/* Bones that are deforming
|
||||
* are regarded to be "skinnable" and are eligible for
|
||||
* auto-skinning.
|
||||
*
|
||||
* This function performs 2 functions:
|
||||
*
|
||||
* a) It returns 1 if the bone is skinnable.
|
||||
* If we loop over all bones with this
|
||||
* function, we can count the number of
|
||||
* skinnable bones.
|
||||
* b) If the pointer data is non null,
|
||||
* it is treated like a handle to a
|
||||
* bone pointer -- the bone pointer
|
||||
* is set to point at this bone, and
|
||||
* the pointer the handle points to
|
||||
* is incremented to point to the
|
||||
* next member of an array of pointers
|
||||
* to bones. This way we can loop using
|
||||
* this function to construct an array of
|
||||
* pointers to bones that point to all
|
||||
* skinnable bones.
|
||||
*/
|
||||
Bone ***hbone;
|
||||
int a, segments;
|
||||
struct Arg {
|
||||
Object *armob;
|
||||
void *list;
|
||||
int heat;
|
||||
bool is_weight_paint;
|
||||
} *data = static_cast<Arg *>(datap);
|
||||
|
||||
bPoseChannel *pose_bone = BKE_pose_channel_find_name(data->armob->pose, bone->name);
|
||||
if (!pose_bone) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(data->is_weight_paint) || !(pose_bone->drawflag & PCHAN_DRAW_HIDDEN)) {
|
||||
if (!(bone->flag & BONE_NO_DEFORM)) {
|
||||
if (data->heat && data->armob->pose && pose_bone) {
|
||||
segments = bone->segments;
|
||||
}
|
||||
else {
|
||||
segments = 1;
|
||||
}
|
||||
|
||||
if (data->list != nullptr) {
|
||||
hbone = reinterpret_cast<Bone ***>(&data->list);
|
||||
|
||||
for (a = 0; a < segments; a++) {
|
||||
**hbone = bone;
|
||||
(*hbone)++;
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void * /*ptr*/)
|
||||
{
|
||||
/* This group creates a vertex group to ob that has the
|
||||
* same name as bone (provided the bone is skinnable).
|
||||
* If such a vertex group already exist the routine exits.
|
||||
*/
|
||||
if (!(bone->flag & BONE_NO_DEFORM)) {
|
||||
if (!BKE_object_defgroup_find_name(ob, bone->name)) {
|
||||
BKE_object_defgroup_add_name(ob, bone->name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap)
|
||||
{
|
||||
/* Bones that are deforming
|
||||
* are regarded to be "skinnable" and are eligible for
|
||||
* auto-skinning.
|
||||
*
|
||||
* This function performs 2 functions:
|
||||
*
|
||||
* a) If the bone is skinnable, it creates
|
||||
* a vertex group for ob that has
|
||||
* the name of the skinnable bone
|
||||
* (if one doesn't exist already).
|
||||
* b) If the pointer data is non null,
|
||||
* it is treated like a handle to a
|
||||
* bDeformGroup pointer -- the
|
||||
* bDeformGroup pointer is set to point
|
||||
* to the deform group with the bone's
|
||||
* name, and the pointer the handle
|
||||
* points to is incremented to point to the
|
||||
* next member of an array of pointers
|
||||
* to bDeformGroups. This way we can loop using
|
||||
* this function to construct an array of
|
||||
* pointers to bDeformGroups, all with names
|
||||
* of skinnable bones.
|
||||
*/
|
||||
bDeformGroup ***hgroup, *defgroup = nullptr;
|
||||
int a, segments;
|
||||
struct Arg {
|
||||
Object *armob;
|
||||
void *list;
|
||||
int heat;
|
||||
bool is_weight_paint;
|
||||
} *data = static_cast<Arg *>(datap);
|
||||
|
||||
if (bone->flag & BONE_NO_DEFORM) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bArmature *arm = id_cast<bArmature *>(data->armob->data);
|
||||
const bPoseChannel *pose_bone = BKE_pose_channel_find_name(data->armob->pose, bone->name);
|
||||
if (!pose_bone) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->is_weight_paint && (pose_bone->drawflag & PCHAN_DRAW_HIDDEN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->heat) {
|
||||
segments = bone->segments;
|
||||
}
|
||||
else {
|
||||
segments = 1;
|
||||
}
|
||||
|
||||
if (!data->is_weight_paint ||
|
||||
(ANIM_bone_in_visible_collection(arm, bone) && (pose_bone->flag & POSE_SELECTED)))
|
||||
{
|
||||
if (!(defgroup = BKE_object_defgroup_find_name(ob, bone->name))) {
|
||||
defgroup = BKE_object_defgroup_add_name(ob, bone->name);
|
||||
}
|
||||
else if (defgroup->flag & DG_LOCK_WEIGHT) {
|
||||
/* In case vgroup already exists and is locked, do not modify it here. See #43814. */
|
||||
defgroup = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->list != nullptr) {
|
||||
hgroup = reinterpret_cast<bDeformGroup ***>(&data->list);
|
||||
|
||||
for (a = 0; a < segments; a++) {
|
||||
**hgroup = defgroup;
|
||||
(*hgroup)++;
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
static void envelope_bone_weighting(Object *ob,
|
||||
Mesh *mesh,
|
||||
const Span<float3> verts,
|
||||
int numbones,
|
||||
Bone **bonelist,
|
||||
bDeformGroup **dgrouplist,
|
||||
bDeformGroup **dgroupflip,
|
||||
float (*root)[3],
|
||||
float (*tip)[3],
|
||||
const bool *selected,
|
||||
float scale)
|
||||
{
|
||||
/* Create vertex group weights from envelopes */
|
||||
|
||||
bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
||||
bool use_mask = false;
|
||||
|
||||
if ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
|
||||
(mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)))
|
||||
{
|
||||
use_mask = true;
|
||||
}
|
||||
|
||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||
const VArray select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
|
||||
|
||||
/* for each vertex in the mesh */
|
||||
for (int i = 0; i < mesh->verts_num; i++) {
|
||||
|
||||
if (use_mask && !(select_vert && select_vert[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, nullptr, i, use_topology) : -1;
|
||||
|
||||
/* for each skinnable bone */
|
||||
for (int j = 0; j < numbones; j++) {
|
||||
if (selected[j] == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bone *bone = bonelist[j];
|
||||
bDeformGroup *dgroup = dgrouplist[j];
|
||||
|
||||
/* store the distance-factor from the vertex to the bone */
|
||||
float distance = distfactor_to_bone(verts[i],
|
||||
root[j],
|
||||
tip[j],
|
||||
bone->rad_head * scale,
|
||||
bone->rad_tail * scale,
|
||||
bone->dist * scale);
|
||||
|
||||
/* add the vert to the deform group if (weight != 0.0) */
|
||||
if (distance != 0.0f) {
|
||||
ed::object::vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE);
|
||||
}
|
||||
else {
|
||||
ed::object::vgroup_vert_remove(ob, dgroup, i);
|
||||
}
|
||||
|
||||
/* do same for mirror */
|
||||
if (dgroupflip && dgroupflip[j] && iflip != -1) {
|
||||
if (distance != 0.0f) {
|
||||
ed::object::vgroup_vert_add(ob, dgroupflip[j], iflip, distance, WEIGHT_REPLACE);
|
||||
}
|
||||
else {
|
||||
ed::object::vgroup_vert_remove(ob, dgroupflip[j], iflip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_verts_to_dgroups(
|
||||
ReportList *reports, Scene * /*scene*/, Object *ob, Object *par, int heat, const bool mirror)
|
||||
{
|
||||
/* This functions implements the automatic computation of vertex group
|
||||
* weights, either through envelopes or using a heat equilibrium.
|
||||
*
|
||||
* This function can be called both when parenting a mesh to an armature,
|
||||
* or in weight-paint + pose-mode. In the latter case selection is taken
|
||||
* into account and vertex weights can be mirrored.
|
||||
*
|
||||
* The mesh vertex positions used are either the final deformed coords
|
||||
* from the evaluated mesh in weight-paint mode, the final sub-surface coords
|
||||
* when parenting, or simply the original mesh coords.
|
||||
*/
|
||||
|
||||
bArmature *arm = id_cast<bArmature *>(par->data);
|
||||
Bone **bonelist, *bone;
|
||||
bDeformGroup **dgrouplist, **dgroupflip;
|
||||
bDeformGroup *dgroup;
|
||||
bPoseChannel *pchan;
|
||||
Mesh *mesh;
|
||||
Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = nullptr;
|
||||
float (*root)[3], (*tip)[3];
|
||||
Array<float3> verts;
|
||||
bool *selected;
|
||||
int numbones, vertsfilled = 0, segments = 0;
|
||||
const bool wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
|
||||
struct {
|
||||
Object *armob;
|
||||
void *list;
|
||||
int heat;
|
||||
bool is_weight_paint;
|
||||
} looper_data;
|
||||
|
||||
looper_data.armob = par;
|
||||
looper_data.heat = heat;
|
||||
looper_data.list = nullptr;
|
||||
looper_data.is_weight_paint = wpmode;
|
||||
|
||||
if (!par->pose) {
|
||||
BKE_pose_rebuild(nullptr, par, arm, false);
|
||||
}
|
||||
BKE_pose_channels_hash_ensure(par->pose);
|
||||
/* count the number of skinnable bones */
|
||||
numbones = bone_looper(
|
||||
ob, static_cast<Bone *>(arm->bonebase.first), &looper_data, bone_skinnable_cb);
|
||||
|
||||
if (numbones == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BKE_object_defgroup_data_create(ob->data) == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* create an array of pointer to bones that are skinnable
|
||||
* and fill it with all of the skinnable bones */
|
||||
bonelist = MEM_new_array_zeroed<Bone *>(numbones, "bonelist");
|
||||
looper_data.list = bonelist;
|
||||
bone_looper(ob, static_cast<Bone *>(arm->bonebase.first), &looper_data, bone_skinnable_cb);
|
||||
|
||||
/* create an array of pointers to the deform groups that
|
||||
* correspond to the skinnable bones (creating them
|
||||
* as necessary). */
|
||||
dgrouplist = MEM_new_array_zeroed<bDeformGroup *>(numbones, "dgrouplist");
|
||||
dgroupflip = MEM_new_array_zeroed<bDeformGroup *>(numbones, "dgroupflip");
|
||||
|
||||
looper_data.list = dgrouplist;
|
||||
bone_looper(ob, static_cast<Bone *>(arm->bonebase.first), &looper_data, dgroup_skinnable_cb);
|
||||
|
||||
/* create an array of root and tip positions transformed into
|
||||
* global coords */
|
||||
root = MEM_new_array_zeroed<float[3]>(numbones, "root");
|
||||
tip = MEM_new_array_zeroed<float[3]>(numbones, "tip");
|
||||
selected = MEM_new_array_zeroed<bool>(numbones, "selected");
|
||||
|
||||
for (int j = 0; j < numbones; j++) {
|
||||
bone = bonelist[j];
|
||||
dgroup = dgrouplist[j];
|
||||
|
||||
/* handle bbone */
|
||||
if (heat) {
|
||||
if (segments == 0) {
|
||||
segments = 1;
|
||||
bbone = nullptr;
|
||||
|
||||
if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) {
|
||||
if (bone->segments > 1) {
|
||||
segments = bone->segments;
|
||||
BKE_pchan_bbone_spline_setup({pchan, bone}, *arm, true, false, bbone_array);
|
||||
bbone = bbone_array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
segments--;
|
||||
}
|
||||
|
||||
/* compute root and tip */
|
||||
if (bbone) {
|
||||
mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]);
|
||||
if ((segments + 1) < bone->segments) {
|
||||
mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(tip[j], bone->arm_tail);
|
||||
}
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(root[j], bone->arm_head);
|
||||
copy_v3_v3(tip[j], bone->arm_tail);
|
||||
}
|
||||
|
||||
mul_m4_v3(par->object_to_world().ptr(), root[j]);
|
||||
mul_m4_v3(par->object_to_world().ptr(), tip[j]);
|
||||
|
||||
/* set selected */
|
||||
if (wpmode) {
|
||||
if (ANIM_bone_in_visible_collection(arm, bone)) {
|
||||
if ((pchan = BKE_pose_channel_find_name(par->pose, bone->name))) {
|
||||
selected[j] = pchan->flag & POSE_SELECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
selected[j] = true;
|
||||
}
|
||||
|
||||
/* find flipped group */
|
||||
if (dgroup && mirror) {
|
||||
char name_flip[MAXBONENAME];
|
||||
|
||||
BLI_string_flip_side_name(name_flip, dgroup->name, false, sizeof(name_flip));
|
||||
dgroupflip[j] = BKE_object_defgroup_find_name(ob, name_flip);
|
||||
}
|
||||
}
|
||||
|
||||
/* create verts */
|
||||
mesh = id_cast<Mesh *>(ob->data);
|
||||
verts.reinitialize(mesh->verts_num);
|
||||
|
||||
if (BKE_modifiers_findby_type(ob, eModifierType_Subsurf)) {
|
||||
/* Is subdivision-surface on? Lets use the verts on the limit surface then.
|
||||
* = same amount of vertices as mesh, but vertices moved to the
|
||||
* subdivision-surfaced position, like for 'optimal'. */
|
||||
bke::subdiv::calculate_limit_positions(mesh, verts);
|
||||
vertsfilled = 1;
|
||||
}
|
||||
|
||||
/* transform verts to global space */
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
for (int i = 0; i < mesh->verts_num; i++) {
|
||||
if (!vertsfilled) {
|
||||
copy_v3_v3(verts[i], positions[i]);
|
||||
}
|
||||
mul_m4_v3(ob->object_to_world().ptr(), verts[i]);
|
||||
}
|
||||
|
||||
/* compute the weights based on gathered vertices and bones */
|
||||
if (heat) {
|
||||
const char *error = nullptr;
|
||||
|
||||
heat_bone_weighting(ob,
|
||||
mesh,
|
||||
reinterpret_cast<float (*)[3]>(verts.data()),
|
||||
numbones,
|
||||
dgrouplist,
|
||||
dgroupflip,
|
||||
root,
|
||||
tip,
|
||||
selected,
|
||||
&error);
|
||||
if (error) {
|
||||
BKE_report(reports, RPT_WARNING, error);
|
||||
}
|
||||
}
|
||||
else {
|
||||
envelope_bone_weighting(ob,
|
||||
mesh,
|
||||
verts,
|
||||
numbones,
|
||||
bonelist,
|
||||
dgrouplist,
|
||||
dgroupflip,
|
||||
root,
|
||||
tip,
|
||||
selected,
|
||||
mat4_to_scale(par->object_to_world().ptr()));
|
||||
}
|
||||
|
||||
/* only generated in some cases but can call anyway */
|
||||
ED_mesh_mirror_spatial_table_end(ob);
|
||||
|
||||
/* free the memory allocated */
|
||||
MEM_delete(bonelist);
|
||||
MEM_delete(dgrouplist);
|
||||
MEM_delete(dgroupflip);
|
||||
MEM_delete(root);
|
||||
MEM_delete(tip);
|
||||
MEM_delete(selected);
|
||||
}
|
||||
|
||||
void ED_object_vgroup_calc_from_armature(ReportList *reports,
|
||||
Depsgraph * /* depsgraph */,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
Object *par,
|
||||
const int mode,
|
||||
const bool mirror)
|
||||
{
|
||||
/* Lets try to create some vertex groups
|
||||
* based on the bones of the parent armature.
|
||||
*/
|
||||
bArmature *arm = id_cast<bArmature *>(par->data);
|
||||
|
||||
if (mode == ARM_GROUPS_NAME) {
|
||||
const int defbase_tot = BKE_object_defgroup_count(ob);
|
||||
int defbase_add;
|
||||
/* Traverse the bone list, trying to create empty vertex
|
||||
* groups corresponding to the bone.
|
||||
*/
|
||||
defbase_add = bone_looper(
|
||||
ob, static_cast<Bone *>(arm->bonebase.first), nullptr, vgroup_add_unique_bone_cb);
|
||||
|
||||
if (defbase_add) {
|
||||
/* It's possible there are DWeights outside the range of the current
|
||||
* object's deform groups. In this case the new groups won't be empty #33889. */
|
||||
ed::object::vgroup_data_clamp_range(ob->data, defbase_tot);
|
||||
}
|
||||
}
|
||||
else if (ELEM(mode, ARM_GROUPS_ENVELOPE, ARM_GROUPS_AUTO)) {
|
||||
/* Traverse the bone list, trying to create vertex groups
|
||||
* that are populated with the vertices for which the
|
||||
* bone is closest.
|
||||
*/
|
||||
add_verts_to_dgroups(reports, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1010
blender-5.2.0/source/blender/editors/armature/armature_utils.cc
Normal file
1010
blender-5.2.0/source/blender/editors/armature/armature_utils.cc
Normal file
File diff suppressed because it is too large
Load Diff
1314
blender-5.2.0/source/blender/editors/armature/bone_collections.cc
Normal file
1314
blender-5.2.0/source/blender/editors/armature/bone_collections.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_undo_system.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_undo.hh"
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "ANIM_bone_collections.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::animrig;
|
||||
|
||||
/** We only need this locally. */
|
||||
static CLG_LogRef LOG = {"undo.armature"};
|
||||
|
||||
/* Utility functions. */
|
||||
|
||||
/**
|
||||
* Remaps edit-bone collection membership.
|
||||
*
|
||||
* This is intended to be used in combination with ED_armature_ebone_listbase_copy()
|
||||
* and ANIM_bonecoll_listbase_copy() to make a full duplicate of both edit
|
||||
* bones and collections together.
|
||||
*/
|
||||
static void remap_ebone_bone_collection_references(
|
||||
ListBaseT<EditBone> *edit_bones, const Map<BoneCollection *, BoneCollection *> &bcoll_map)
|
||||
{
|
||||
for (EditBone &ebone : *edit_bones) {
|
||||
for (BoneCollectionReference &bcoll_ref : ebone.bone_collections) {
|
||||
bcoll_ref.bcoll = bcoll_map.lookup(bcoll_ref.bcoll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
struct UndoArmature {
|
||||
EditBone *act_edbone;
|
||||
char active_collection_name[MAX_NAME];
|
||||
ListBaseT<EditBone> ebones;
|
||||
BoneCollection **collection_array;
|
||||
int collection_array_num;
|
||||
int collection_root_count;
|
||||
size_t undo_size;
|
||||
};
|
||||
|
||||
static void undoarm_to_editarm(UndoArmature *uarm, bArmature *arm)
|
||||
{
|
||||
/* Copy edit bones. */
|
||||
ED_armature_ebone_listbase_free(arm->edbo, true);
|
||||
ED_armature_ebone_listbase_copy(arm->edbo, &uarm->ebones, true);
|
||||
|
||||
/* Active bone. */
|
||||
if (uarm->act_edbone) {
|
||||
EditBone *ebone;
|
||||
ebone = uarm->act_edbone;
|
||||
arm->act_edbone = ebone->temp.ebone;
|
||||
}
|
||||
else {
|
||||
arm->act_edbone = nullptr;
|
||||
}
|
||||
|
||||
ED_armature_ebone_listbase_temp_clear(arm->edbo);
|
||||
|
||||
/* Before freeing the old bone collections, copy their 'expanded' flag. This
|
||||
* flag is not supposed to be restored with any undo steps. */
|
||||
bonecolls_copy_expanded_flag(Span(uarm->collection_array, uarm->collection_array_num),
|
||||
arm->collections_span());
|
||||
|
||||
/* Copy bone collections. */
|
||||
ANIM_bonecoll_array_free(&arm->collection_array, &arm->collection_array_num, true);
|
||||
auto bcoll_map = ANIM_bonecoll_array_copy_no_membership(&arm->collection_array,
|
||||
&arm->collection_array_num,
|
||||
uarm->collection_array,
|
||||
uarm->collection_array_num,
|
||||
true);
|
||||
arm->collection_root_count = uarm->collection_root_count;
|
||||
|
||||
/* Always do a lookup-by-name and assignment. Even when the name of the active collection is
|
||||
* still the same, the order may have changed and thus the index needs to be updated. */
|
||||
BoneCollection *active_bcoll = ANIM_armature_bonecoll_get_by_name(arm,
|
||||
uarm->active_collection_name);
|
||||
ANIM_armature_bonecoll_active_set(arm, active_bcoll);
|
||||
|
||||
remap_ebone_bone_collection_references(arm->edbo, bcoll_map);
|
||||
|
||||
ANIM_armature_runtime_refresh(arm);
|
||||
}
|
||||
|
||||
static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm)
|
||||
{
|
||||
BLI_assert(BLI_array_is_zeroed(uarm, 1));
|
||||
|
||||
/* Copy edit bones. */
|
||||
ED_armature_ebone_listbase_copy(&uarm->ebones, arm->edbo, false);
|
||||
|
||||
/* Active bone. */
|
||||
if (arm->act_edbone) {
|
||||
EditBone *ebone = arm->act_edbone;
|
||||
uarm->act_edbone = ebone->temp.ebone;
|
||||
}
|
||||
|
||||
ED_armature_ebone_listbase_temp_clear(&uarm->ebones);
|
||||
|
||||
/* Copy bone collections. */
|
||||
auto bcoll_map = ANIM_bonecoll_array_copy_no_membership(&uarm->collection_array,
|
||||
&uarm->collection_array_num,
|
||||
arm->collection_array,
|
||||
arm->collection_array_num,
|
||||
false);
|
||||
STRNCPY(uarm->active_collection_name, arm->active_collection_name);
|
||||
uarm->collection_root_count = arm->collection_root_count;
|
||||
|
||||
/* Point the new edit bones at the new collections. */
|
||||
remap_ebone_bone_collection_references(&uarm->ebones, bcoll_map);
|
||||
|
||||
/* Undo size.
|
||||
* TODO: include size of ID-properties. */
|
||||
uarm->undo_size = 0;
|
||||
for (EditBone &ebone : uarm->ebones) {
|
||||
uarm->undo_size += sizeof(EditBone);
|
||||
uarm->undo_size += sizeof(BoneCollectionReference) * ebone.bone_collections.count();
|
||||
}
|
||||
/* Size of the bone collections + the size of the pointers to those
|
||||
* bone collections in the bone collection array. */
|
||||
uarm->undo_size += (sizeof(BoneCollection) + sizeof(BoneCollection *)) *
|
||||
uarm->collection_array_num;
|
||||
|
||||
return uarm;
|
||||
}
|
||||
|
||||
static void undoarm_free_data(UndoArmature *uarm)
|
||||
{
|
||||
ED_armature_ebone_listbase_free(&uarm->ebones, false);
|
||||
ANIM_bonecoll_array_free(&uarm->collection_array, &uarm->collection_array_num, false);
|
||||
}
|
||||
|
||||
static Object *editarm_object_from_context(bContext *C)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
|
||||
Object *obedit = BKE_view_layer_edit_object_get(view_layer);
|
||||
if (obedit && obedit->type == OB_ARMATURE) {
|
||||
bArmature *arm = id_cast<bArmature *>(obedit->data);
|
||||
if (arm->edbo != nullptr) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
*
|
||||
* \note This is similar for all edit-mode types.
|
||||
* \{ */
|
||||
|
||||
struct ArmatureUndoStep_Elem {
|
||||
ArmatureUndoStep_Elem *next, *prev;
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoArmature data;
|
||||
};
|
||||
|
||||
struct ArmatureUndoStep {
|
||||
UndoStep step;
|
||||
/** See #ED_undo_object_editmode_validate_scene_from_windows code comment for details. */
|
||||
UndoRefID_Scene scene_ref;
|
||||
ArmatureUndoStep_Elem *elems;
|
||||
uint elems_len;
|
||||
};
|
||||
|
||||
static bool armature_undosys_poll(bContext *C)
|
||||
{
|
||||
return editarm_object_from_context(C) != nullptr;
|
||||
}
|
||||
|
||||
static bool armature_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
|
||||
{
|
||||
ArmatureUndoStep *us = reinterpret_cast<ArmatureUndoStep *>(us_p);
|
||||
|
||||
/* Important not to use the 3D view when getting objects because all objects
|
||||
* outside of this list will be moved out of edit-mode when reading back undo steps. */
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(*bmain, scene, view_layer);
|
||||
|
||||
us->scene_ref.ptr = scene;
|
||||
us->elems = MEM_new_array_zeroed<ArmatureUndoStep_Elem>(objects.size(), __func__);
|
||||
us->elems_len = objects.size();
|
||||
|
||||
for (uint i = 0; i < objects.size(); i++) {
|
||||
Object *ob = objects[i];
|
||||
ArmatureUndoStep_Elem *elem = &us->elems[i];
|
||||
|
||||
elem->obedit_ref.ptr = ob;
|
||||
bArmature *arm = id_cast<bArmature *>(elem->obedit_ref.ptr->data);
|
||||
undoarm_from_editarm(&elem->data, arm);
|
||||
arm->needs_flush_to_id = 1;
|
||||
us->step.data_size += elem->data.undo_size;
|
||||
}
|
||||
|
||||
bmain->is_memfile_undo_flush_needed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void armature_undosys_step_decode(
|
||||
bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
|
||||
{
|
||||
ArmatureUndoStep *us = reinterpret_cast<ArmatureUndoStep *>(us_p);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
ED_undo_object_editmode_validate_scene_from_windows(
|
||||
CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
|
||||
ED_undo_object_editmode_restore_helper(
|
||||
scene, view_layer, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
|
||||
|
||||
BLI_assert(BKE_object_is_in_editmode(us->elems[0].obedit_ref.ptr));
|
||||
|
||||
for (uint i = 0; i < us->elems_len; i++) {
|
||||
ArmatureUndoStep_Elem *elem = &us->elems[i];
|
||||
Object *obedit = elem->obedit_ref.ptr;
|
||||
bArmature *arm = id_cast<bArmature *>(obedit->data);
|
||||
if (arm->edbo == nullptr) {
|
||||
/* Should never fail, may not crash but can give odd behavior. */
|
||||
CLOG_ERROR(&LOG,
|
||||
"name='%s', failed to enter edit-mode for object '%s', undo state invalid",
|
||||
us_p->name,
|
||||
obedit->id.name);
|
||||
continue;
|
||||
}
|
||||
undoarm_to_editarm(&elem->data, arm);
|
||||
arm->needs_flush_to_id = 1;
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
/* The first element is always active */
|
||||
ED_undo_object_set_active_or_warn(
|
||||
*bmain, scene, view_layer, us->elems[0].obedit_ref.ptr, us_p->name, &LOG);
|
||||
|
||||
/* Check after setting active (unless undoing into another scene). */
|
||||
BLI_assert(armature_undosys_poll(C) || (scene != CTX_data_scene(C)));
|
||||
|
||||
bmain->is_memfile_undo_flush_needed = true;
|
||||
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, nullptr);
|
||||
}
|
||||
|
||||
static void armature_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
ArmatureUndoStep *us = reinterpret_cast<ArmatureUndoStep *>(us_p);
|
||||
|
||||
for (uint i = 0; i < us->elems_len; i++) {
|
||||
ArmatureUndoStep_Elem *elem = &us->elems[i];
|
||||
undoarm_free_data(&elem->data);
|
||||
}
|
||||
MEM_delete(us->elems);
|
||||
}
|
||||
|
||||
static void armature_undosys_foreach_ID_ref(UndoStep *us_p,
|
||||
UndoTypeForEachIDRefFn foreach_ID_ref_fn,
|
||||
void *user_data)
|
||||
{
|
||||
ArmatureUndoStep *us = reinterpret_cast<ArmatureUndoStep *>(us_p);
|
||||
|
||||
foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&us->scene_ref));
|
||||
for (uint i = 0; i < us->elems_len; i++) {
|
||||
ArmatureUndoStep_Elem *elem = &us->elems[i];
|
||||
foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&elem->obedit_ref));
|
||||
}
|
||||
}
|
||||
|
||||
void ED_armature_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Armature";
|
||||
ut->poll = armature_undosys_poll;
|
||||
ut->step_encode = armature_undosys_step_encode;
|
||||
ut->step_decode = armature_undosys_step_decode;
|
||||
ut->step_free = armature_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref;
|
||||
|
||||
ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE;
|
||||
|
||||
ut->step_size = sizeof(ArmatureUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
1803
blender-5.2.0/source/blender/editors/armature/meshlaplacian.cc
Normal file
1803
blender-5.2.0/source/blender/editors/armature/meshlaplacian.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
* BIF_meshlaplacian.h: Algorithms using the mesh laplacian.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
// #define RIGID_DEFORM
|
||||
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct bDeformGroup;
|
||||
|
||||
#ifdef RIGID_DEFORM
|
||||
struct EditMesh;
|
||||
#endif
|
||||
|
||||
/* Laplacian System */
|
||||
|
||||
struct LaplacianSystem;
|
||||
struct LaplacianSystem;
|
||||
|
||||
void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned);
|
||||
void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3);
|
||||
|
||||
void laplacian_begin_solve(LaplacianSystem *sys, int index);
|
||||
void laplacian_add_right_hand_side(LaplacianSystem *sys, int v, float value);
|
||||
int laplacian_system_solve(LaplacianSystem *sys);
|
||||
float laplacian_system_get_solution(LaplacianSystem *sys, int v);
|
||||
|
||||
/* Heat Weighting */
|
||||
|
||||
void heat_bone_weighting(struct Object *ob,
|
||||
struct Mesh *mesh,
|
||||
float (*verts)[3],
|
||||
int numbones,
|
||||
struct bDeformGroup **dgrouplist,
|
||||
struct bDeformGroup **dgroupflip,
|
||||
float (*root)[3],
|
||||
float (*tip)[3],
|
||||
const bool *selected,
|
||||
const char **r_error_str);
|
||||
|
||||
#ifdef RIGID_DEFORM
|
||||
/* As-Rigid-As-Possible Deformation */
|
||||
|
||||
void rigid_deform_begin(struct EditMesh *em);
|
||||
void rigid_deform_iteration(void);
|
||||
void rigid_deform_end(int cancel);
|
||||
#endif
|
||||
|
||||
/* Harmonic Coordinates */
|
||||
|
||||
/* ED_mesh_deform_bind_callback(...) defined in ED_armature.hh */
|
||||
|
||||
} // namespace blender
|
||||
808
blender-5.2.0/source/blender/editors/armature/pose_edit.cc
Normal file
808
blender-5.2.0/source/blender/editors/armature/pose_edit.cc
Normal file
@@ -0,0 +1,808 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
* Pose Mode API's and Operators for Pose Mode armatures.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_anim_visualization.h"
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_anim_api.hh"
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_keyframing.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "ANIM_armature.hh"
|
||||
#include "ANIM_bone_collections.hh"
|
||||
#include "ANIM_keyframing.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#undef DEBUG_TIME
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
# include "BLI_time_utildefines.h"
|
||||
#endif
|
||||
|
||||
Object *ED_pose_object_from_context(bContext *C)
|
||||
{
|
||||
/* NOTE: matches logic with #ED_operator_posemode_context(). */
|
||||
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
Object *ob;
|
||||
|
||||
/* Since this call may also be used from the buttons window,
|
||||
* we need to check for where to get the object. */
|
||||
if (area && area->spacetype == SPACE_PROPERTIES) {
|
||||
ob = ed::object::context_active_object(C);
|
||||
}
|
||||
else {
|
||||
ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
}
|
||||
|
||||
return ob;
|
||||
}
|
||||
|
||||
bool ED_object_posemode_enter_ex(Main *bmain, Object *ob)
|
||||
{
|
||||
BLI_assert(BKE_id_is_editable(bmain, &ob->id));
|
||||
bool ok = false;
|
||||
|
||||
switch (ob->type) {
|
||||
case OB_ARMATURE:
|
||||
ob->restore_mode = ob->mode;
|
||||
ob->mode |= OB_MODE_POSE;
|
||||
|
||||
/* Inform all evaluated versions that we changed the mode. */
|
||||
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
ok = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
bool ED_object_posemode_enter(bContext *C, Object *ob)
|
||||
{
|
||||
ReportList *reports = CTX_wm_reports(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (!BKE_id_is_editable(bmain, &ob->id)) {
|
||||
BKE_report(reports, RPT_WARNING, "Cannot pose libdata");
|
||||
return false;
|
||||
}
|
||||
bool ok = ED_object_posemode_enter_ex(bmain, ob);
|
||||
if (ok) {
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, nullptr);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ED_object_posemode_exit_ex(Main *bmain, Object *ob)
|
||||
{
|
||||
bool ok = false;
|
||||
if (ob) {
|
||||
ob->restore_mode = ob->mode;
|
||||
ob->mode &= ~OB_MODE_POSE;
|
||||
|
||||
/* Inform all evaluated versions that we changed the mode. */
|
||||
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
bool ED_object_posemode_exit(bContext *C, Object *ob)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bool ok = ED_object_posemode_exit_ex(bmain, ob);
|
||||
if (ok) {
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Motion Paths */
|
||||
|
||||
void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, eAnimvizCalcRange range)
|
||||
{
|
||||
/* Transform doesn't always have context available to do update. */
|
||||
if (C == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
Vector<MPathTarget *> targets;
|
||||
/* set flag to force recalc, then grab the relevant bones to target */
|
||||
ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS;
|
||||
animviz_build_motionpath_targets(ob, targets);
|
||||
|
||||
/* recalculate paths, then free */
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(pose_path_calc);
|
||||
#endif
|
||||
|
||||
Depsgraph *depsgraph = animviz_depsgraph_build(bmain, scene, view_layer, targets);
|
||||
animviz_calc_motionpaths(depsgraph, scene, targets, range);
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(pose_path_calc);
|
||||
#endif
|
||||
|
||||
animviz_free_motionpath_targets(targets);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
|
||||
/* Free temporary depsgraph. */
|
||||
DEG_graph_free(depsgraph);
|
||||
}
|
||||
|
||||
/* show popup to determine settings */
|
||||
static wmOperatorStatus pose_calculate_paths_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
|
||||
if (ELEM(nullptr, ob, ob->pose)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* set default settings from existing/stored settings */
|
||||
{
|
||||
bAnimVizSettings *avs = &ob->pose->avs;
|
||||
|
||||
PointerRNA avs_ptr = RNA_pointer_create_discrete(nullptr, RNA_AnimVizMotionPaths, avs);
|
||||
RNA_enum_set(op->ptr, "display_type", RNA_enum_get(&avs_ptr, "type"));
|
||||
RNA_enum_set(op->ptr, "range", RNA_enum_get(&avs_ptr, "range"));
|
||||
RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location"));
|
||||
}
|
||||
|
||||
/* show popup dialog to allow editing of range... */
|
||||
/* FIXME: hard-coded dimensions here are just arbitrary. */
|
||||
return WM_operator_props_dialog_popup(
|
||||
C, op, 270, IFACE_("Calculate Paths for the Selected Bones"), IFACE_("Calculate"));
|
||||
}
|
||||
|
||||
/**
|
||||
* For the object with pose/action: create path curves for selected bones
|
||||
* This recalculates the WHOLE path within the `pchan->pathsf` and `pchan->pathef` range.
|
||||
*/
|
||||
static wmOperatorStatus pose_calculate_paths_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
if (ELEM(nullptr, ob, ob->pose)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* grab baking settings from operator settings */
|
||||
{
|
||||
bAnimVizSettings *avs = &ob->pose->avs;
|
||||
|
||||
avs->path_type = eMotionPaths_Types(RNA_enum_get(op->ptr, "display_type"));
|
||||
avs->path_range = eMotionPath_Ranges(RNA_enum_get(op->ptr, "range"));
|
||||
animviz_motionpath_compute_range(ob, scene);
|
||||
|
||||
PointerRNA avs_ptr = RNA_pointer_create_discrete(nullptr, RNA_AnimVizMotionPaths, avs);
|
||||
RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location"));
|
||||
}
|
||||
|
||||
/* set up path data for bones being calculated */
|
||||
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
|
||||
/* verify makes sure that the selected bone has a bone with the appropriate settings */
|
||||
animviz_verify_motionpaths(op->reports, scene, ob, pchan);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(recalc_pose_paths);
|
||||
#endif
|
||||
|
||||
/* Calculate the bones that now have motion-paths. */
|
||||
/* TODO: only make for the selected bones? */
|
||||
ED_pose_recalculate_paths(C, scene, ob, ANIMVIZ_CALC_RANGE_FULL);
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(recalc_pose_paths);
|
||||
#endif
|
||||
|
||||
/* notifiers for updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_paths_calculate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Calculate Bone Paths";
|
||||
ot->idname = "POSE_OT_paths_calculate";
|
||||
ot->description = "Calculate paths for the selected bones";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->invoke = pose_calculate_paths_invoke;
|
||||
ot->exec = pose_calculate_paths_exec;
|
||||
ot->poll = ED_operator_posemode_exclusive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_enum(ot->srna,
|
||||
"display_type",
|
||||
rna_enum_motionpath_display_type_items,
|
||||
MOTIONPATH_TYPE_RANGE,
|
||||
"Display Type",
|
||||
"");
|
||||
RNA_def_enum(ot->srna,
|
||||
"range",
|
||||
rna_enum_motionpath_range_items,
|
||||
MOTIONPATH_RANGE_SCENE,
|
||||
"Computation Range",
|
||||
"");
|
||||
|
||||
RNA_def_enum(ot->srna,
|
||||
"bake_location",
|
||||
rna_enum_motionpath_bake_location_items,
|
||||
MOTIONPATH_BAKE_HEADS,
|
||||
"Bake Location",
|
||||
"Which point on the bones is used when calculating paths");
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
static bool pose_update_paths_poll(bContext *C)
|
||||
{
|
||||
if (ED_operator_posemode_exclusive(C)) {
|
||||
Object *ob = ed::object::context_active_object(C);
|
||||
return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static wmOperatorStatus pose_update_paths_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
if (ELEM(nullptr, ob, scene)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
animviz_motionpath_compute_range(ob, scene);
|
||||
|
||||
/* set up path data for bones being calculated */
|
||||
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
|
||||
animviz_verify_motionpaths(op->reports, scene, ob, pchan);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* Calculate the bones that now have motion-paths. */
|
||||
/* TODO: only make for the selected bones? */
|
||||
ED_pose_recalculate_paths(C, scene, ob, ANIMVIZ_CALC_RANGE_FULL);
|
||||
|
||||
/* notifiers for updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_paths_update(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Update Bone Paths";
|
||||
ot->idname = "POSE_OT_paths_update";
|
||||
ot->description = "Recalculate paths for bones that already have them";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = pose_update_paths_exec;
|
||||
ot->poll = pose_update_paths_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
/* for the object with pose/action: clear path curves for selected bones only */
|
||||
static void pose_clear_paths(Object *ob, bool only_selected)
|
||||
{
|
||||
bool skipped = false;
|
||||
|
||||
if (ELEM(nullptr, ob, ob->pose)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* free the motionpath blocks for all bones - This is easier for users to quickly clear all */
|
||||
for (bPoseChannel &pchan : ob->pose->chanbase) {
|
||||
if (pchan.mpath) {
|
||||
if ((only_selected == false) || (pchan.flag & POSE_SELECTED)) {
|
||||
animviz_free_motionpath(pchan.mpath);
|
||||
pchan.mpath = nullptr;
|
||||
}
|
||||
else {
|
||||
skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if nothing was skipped, there should be no paths left! */
|
||||
if (skipped == false) {
|
||||
ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
|
||||
}
|
||||
|
||||
/* tag armature object for copy-on-eval - so removed paths don't still show */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
|
||||
/* Operator callback - wrapper for the back-end function. */
|
||||
static wmOperatorStatus pose_clear_paths_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
|
||||
|
||||
/* only continue if there's an object */
|
||||
if (ELEM(nullptr, ob, ob->pose)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* use the backend function for this */
|
||||
pose_clear_paths(ob, only_selected);
|
||||
|
||||
/* notifiers for updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static std::string pose_clear_paths_get_description(bContext * /*C*/,
|
||||
wmOperatorType * /*ot*/,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
const bool only_selected = RNA_boolean_get(ptr, "only_selected");
|
||||
if (only_selected) {
|
||||
return TIP_("Clear motion paths of selected bones");
|
||||
}
|
||||
return TIP_("Clear motion paths of all bones");
|
||||
}
|
||||
|
||||
void POSE_OT_paths_clear(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Clear Bone Paths";
|
||||
ot->idname = "POSE_OT_paths_clear";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = pose_clear_paths_exec;
|
||||
ot->poll = ED_operator_posemode_exclusive;
|
||||
ot->get_description = pose_clear_paths_get_description;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"only_selected",
|
||||
false,
|
||||
"Only Selected",
|
||||
"Only clear motion paths of selected bones");
|
||||
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
static wmOperatorStatus pose_update_paths_range_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
|
||||
|
||||
if (ELEM(nullptr, scene, ob, ob->pose)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Use Preview Range or Full Frame Range - whichever is in use. */
|
||||
ob->pose->avs.path_sf = scene->playback_start();
|
||||
ob->pose->avs.path_ef = scene->playback_end();
|
||||
|
||||
/* tag for updates */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW_ANIMVIZ, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_paths_range_update(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Update Range from Scene";
|
||||
ot->idname = "POSE_OT_paths_range_update";
|
||||
ot->description = "Update frame range for motion paths from the Scene's current frame range";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = pose_update_paths_range_exec;
|
||||
ot->poll = ED_operator_posemode_exclusive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
static wmOperatorStatus pose_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);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers");
|
||||
|
||||
FOREACH_OBJECT_IN_MODE_BEGIN (bmain, scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
|
||||
bArmature *arm = id_cast<bArmature *>(ob->data);
|
||||
ListBaseT<LinkData> bones_names = {nullptr};
|
||||
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) {
|
||||
BLI_addtail(&bones_names, BLI_genericNodeN(pchan->name));
|
||||
}
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
|
||||
|
||||
ED_armature_bones_flip_names(bmain, arm, &bones_names, do_strip_numbers);
|
||||
|
||||
bones_names.free_no_destruct();
|
||||
|
||||
/* Since we renamed stuff... */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_RENAME, ob->data);
|
||||
|
||||
/* Update animation channels */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data);
|
||||
}
|
||||
FOREACH_OBJECT_IN_MODE_END;
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_flip_names(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Flip Names";
|
||||
ot->idname = "POSE_OT_flip_names";
|
||||
ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = pose_flip_names_exec;
|
||||
ot->poll = ED_operator_posemode_local;
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* ------------------ */
|
||||
|
||||
static wmOperatorStatus pose_autoside_names_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
char newname[MAXBONENAME];
|
||||
short axis = RNA_enum_get(op->ptr, "axis");
|
||||
Object *ob_prev = nullptr;
|
||||
|
||||
/* loop through selected bones, auto-naming them */
|
||||
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
|
||||
bArmature *arm = id_cast<bArmature *>(ob->data);
|
||||
const Bone *bone = pchan->bone_get(*ob);
|
||||
STRNCPY_UTF8(newname, pchan->name);
|
||||
if (bone_autoside_name(newname, 1, axis, bone->head[axis], bone->tail[axis])) {
|
||||
ED_armature_bone_rename(bmain, arm, pchan->name, newname);
|
||||
}
|
||||
|
||||
if (ob_prev != ob) {
|
||||
/* Since we renamed stuff... */
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_RENAME, ob->data);
|
||||
|
||||
/* Update animation channels */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, ob->data);
|
||||
ob_prev = ob;
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_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 = "POSE_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 = pose_autoside_names_exec;
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* settings */
|
||||
ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis to tag names with");
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
static wmOperatorStatus pose_bone_rotmode_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const eRotationModes mode = eRotationModes(RNA_enum_get(op->ptr, "type"));
|
||||
Object *prev_ob = nullptr;
|
||||
|
||||
/* Set rotation mode of selected bones. */
|
||||
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
|
||||
/* use API Method for conversions... */
|
||||
BKE_rotMode_change_values(
|
||||
pchan->quat, pchan->eul, pchan->rotAxis, &pchan->rotAngle, pchan->rotmode, mode);
|
||||
|
||||
/* finally, set the new rotation type */
|
||||
pchan->rotmode = mode;
|
||||
|
||||
if (prev_ob != ob) {
|
||||
/* Notifiers and updates. */
|
||||
DEG_id_tag_update(reinterpret_cast<ID *>(ob), ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
prev_ob = ob;
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_rotation_mode_set(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Set Rotation Mode";
|
||||
ot->idname = "POSE_OT_rotation_mode_set";
|
||||
ot->description = "Set the rotation representation used by selected bones";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->exec = pose_bone_rotmode_exec;
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(
|
||||
ot->srna, "type", rna_enum_object_rotation_mode_items, 0, "Rotation Mode", "");
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Show/Hide Bones */
|
||||
|
||||
/* active object is armature in posemode, poll checked */
|
||||
static wmOperatorStatus pose_hide_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = BKE_object_pose_array_get_unique(
|
||||
*bmain, scene, view_layer, CTX_wm_view3d(C));
|
||||
bool changed_multi = false;
|
||||
|
||||
const int hide_select = !RNA_boolean_get(op->ptr, "unselected");
|
||||
|
||||
for (Object *ob_iter : objects) {
|
||||
bool changed = false;
|
||||
bArmature *arm = id_cast<bArmature *>(ob_iter->data);
|
||||
for (bPoseChannel &pchan : ob_iter->pose->chanbase) {
|
||||
if (!ANIM_bone_in_visible_collection(arm, pchan.bone_get(*ob_iter))) {
|
||||
continue;
|
||||
}
|
||||
if (((pchan.flag & POSE_SELECTED) != 0) != hide_select) {
|
||||
continue;
|
||||
}
|
||||
pchan.drawflag |= PCHAN_DRAW_HIDDEN;
|
||||
animrig::bone_deselect(&pchan);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
changed_multi = true;
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void POSE_OT_hide(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Hide Selected";
|
||||
ot->idname = "POSE_OT_hide";
|
||||
ot->description = "Tag selected bones to not be visible in Pose Mode";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = pose_hide_exec;
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* props */
|
||||
RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "");
|
||||
}
|
||||
|
||||
/* active object is armature in posemode, poll checked */
|
||||
static wmOperatorStatus pose_reveal_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = BKE_object_pose_array_get_unique(
|
||||
*bmain, scene, view_layer, CTX_wm_view3d(C));
|
||||
bool changed_multi = false;
|
||||
const bool select = RNA_boolean_get(op->ptr, "select");
|
||||
|
||||
for (Object *ob_iter : objects) {
|
||||
bArmature *arm = id_cast<bArmature *>(ob_iter->data);
|
||||
|
||||
bool changed = false;
|
||||
for (bPoseChannel &pchan : ob_iter->pose->chanbase) {
|
||||
const Bone *bone = pchan.bone_get(*ob_iter);
|
||||
if (!ANIM_bone_in_visible_collection(arm, bone)) {
|
||||
continue;
|
||||
}
|
||||
if ((pchan.drawflag & PCHAN_DRAW_HIDDEN) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!(bone->flag & BONE_UNSELECTABLE)) {
|
||||
SET_FLAG_FROM_TEST(pchan.flag, select, POSE_SELECTED);
|
||||
}
|
||||
pchan.drawflag &= ~PCHAN_DRAW_HIDDEN;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
changed_multi = true;
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob_iter);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_PARAMETERS);
|
||||
}
|
||||
}
|
||||
|
||||
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void POSE_OT_reveal(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Reveal Selected";
|
||||
ot->idname = "POSE_OT_reveal";
|
||||
ot->description = "Reveal all bones hidden in Pose Mode";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = pose_reveal_exec;
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(ot->srna, "select", true, "Select", "");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Flip Quaternions
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus pose_flip_quats_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
bool changed_multi = false;
|
||||
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
FOREACH_OBJECT_IN_MODE_BEGIN (bmain, scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter)
|
||||
{
|
||||
bool changed = false;
|
||||
/* loop through all selected pchans, flipping and keying (as needed) */
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) {
|
||||
/* only if bone is using quaternion rotation */
|
||||
if (pchan->rotmode == ROT_MODE_QUAT) {
|
||||
changed = true;
|
||||
/* quaternions have 720 degree range */
|
||||
negate_v4(pchan->quat);
|
||||
|
||||
animrig::autokeyframe_pose_channel(
|
||||
C, scene, ob_iter, pchan, {{"rotation_quaternion"}}, false);
|
||||
}
|
||||
}
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
|
||||
|
||||
if (changed) {
|
||||
changed_multi = true;
|
||||
/* notifiers and updates */
|
||||
DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter);
|
||||
}
|
||||
}
|
||||
FOREACH_OBJECT_IN_MODE_END;
|
||||
|
||||
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void POSE_OT_quaternions_flip(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Flip Quaternions";
|
||||
ot->idname = "POSE_OT_quaternions_flip";
|
||||
ot->description =
|
||||
"Flip quaternion values to achieve desired rotations, while maintaining the same "
|
||||
"orientations";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = pose_flip_quats_exec;
|
||||
ot->poll = ED_operator_posemode;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
749
blender-5.2.0/source/blender/editors/armature/pose_lib_2.cc
Normal file
749
blender-5.2.0/source/blender/editors/armature/pose_lib_2.cc
Normal file
@@ -0,0 +1,749 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_pose_backup.h"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
#include "ED_asset.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_keyframing.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "ANIM_action.hh"
|
||||
#include "ANIM_action_legacy.hh"
|
||||
#include "ANIM_armature.hh"
|
||||
#include "ANIM_keyframing.hh"
|
||||
#include "ANIM_keyingsets.hh"
|
||||
#include "ANIM_pose.hh"
|
||||
#include "ANIM_rna.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
enum ePoseBlendState {
|
||||
POSE_BLEND_INIT,
|
||||
POSE_BLEND_BLENDING,
|
||||
POSE_BLEND_ORIGINAL,
|
||||
POSE_BLEND_CONFIRM,
|
||||
POSE_BLEND_CANCEL,
|
||||
};
|
||||
|
||||
struct PoseBlendData {
|
||||
ePoseBlendState state;
|
||||
bool needs_redraw;
|
||||
|
||||
struct {
|
||||
bool use_release_confirm;
|
||||
int init_event_type;
|
||||
|
||||
} release_confirm_info;
|
||||
|
||||
/* For temp-loading the Action from the pose library. */
|
||||
AssetTempIDConsumer *temp_id_consumer;
|
||||
|
||||
/* Blend factor for interpolating between current and given pose.
|
||||
* 1.0 means "100% pose asset". Negative values and values > 1.0 will be used as-is, and can
|
||||
* cause interesting effects. */
|
||||
float blend_factor;
|
||||
bool is_flipped;
|
||||
PoseBackup *pose_backup;
|
||||
|
||||
Vector<Object *> objects; /* Objects to work on. */
|
||||
bAction *act; /* Pose to blend into the current pose. */
|
||||
bAction *act_flipped; /* Flipped copy of `act`. */
|
||||
|
||||
Scene *scene; /* For auto-keying. */
|
||||
ScrArea *area; /* For drawing status text. */
|
||||
|
||||
tSlider *slider; /* Slider UI and event handling. */
|
||||
|
||||
/** Info-text to print in header. */
|
||||
char headerstr[UI_MAX_DRAW_STR];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the bAction that should be blended.
|
||||
* This is either `pbd->act` or `pbd->act_flipped`, depending on `is_flipped`.
|
||||
*/
|
||||
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
|
||||
{
|
||||
return pbd->is_flipped ? pbd->act_flipped : pbd->act;
|
||||
}
|
||||
|
||||
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
||||
static void poselib_backup_posecopy(PoseBlendData *pbd)
|
||||
{
|
||||
bAction *action = poselib_action_to_blend(pbd);
|
||||
pbd->pose_backup = BKE_pose_backup_create_selected_bones(pbd->objects, action);
|
||||
|
||||
if (pbd->state == POSE_BLEND_INIT) {
|
||||
/* Ready for blending now. */
|
||||
pbd->state = POSE_BLEND_BLENDING;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
|
||||
/* Auto-key/tag bones affected by the pose Action. */
|
||||
static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd)
|
||||
{
|
||||
for (Object *ob : pbd->objects) {
|
||||
if (!animrig::autokeyframe_cfra_can_key(scene, &ob->id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AnimData *adt = BKE_animdata_from_id(&ob->id);
|
||||
if (adt != nullptr && adt->action != nullptr &&
|
||||
!BKE_id_is_editable(CTX_data_main(C), &adt->action->id))
|
||||
{
|
||||
/* Changes to linked-in Actions are not allowed. */
|
||||
return;
|
||||
}
|
||||
|
||||
bPose *pose = ob->pose;
|
||||
bAction *act = poselib_action_to_blend(pbd);
|
||||
const bArmature *armature = id_cast<const bArmature *>(ob->data);
|
||||
|
||||
animrig::Slot &slot = animrig::get_best_pose_slot_for_id(ob->id, act->wrap());
|
||||
|
||||
/* Storing which pose bones were already keyed since multiple FCurves will probably exist per
|
||||
* pose bone. */
|
||||
Set<bPoseChannel *> keyed_pose_bones;
|
||||
auto autokey_pose_bones = [&](FCurve * /* fcu */, const char *bone_name) {
|
||||
bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone_name);
|
||||
if (!pchan) {
|
||||
/* This bone cannot be found any more. This is fine, this can happen
|
||||
* when F-Curves for a bone are included in a pose asset, and later the
|
||||
* bone itself was renamed or removed. */
|
||||
return;
|
||||
}
|
||||
if (BKE_pose_backup_is_selection_relevant(pbd->pose_backup) &&
|
||||
!animrig::bone_is_selected(armature, {pchan, pchan->bone_get(*ob)}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (keyed_pose_bones.contains(pchan)) {
|
||||
return;
|
||||
}
|
||||
/* This mimics the Whole Character Keying Set that was used here previously. In the future we
|
||||
* could only key rna paths of FCurves that are actually in the applied pose. */
|
||||
PointerRNA pose_bone_pointer = RNA_pointer_create_discrete(&ob->id, RNA_PoseBone, pchan);
|
||||
Vector<RNAPath> rna_paths = animrig::get_keyable_id_property_paths(pose_bone_pointer);
|
||||
rna_paths.append({"location"});
|
||||
const StringRefNull rotation_mode_path = animrig::get_rotation_mode_path(
|
||||
eRotationModes(pchan->rotmode));
|
||||
rna_paths.append({rotation_mode_path});
|
||||
rna_paths.append({"scale"});
|
||||
animrig::autokeyframe_pose_channel(C, scene, ob, pchan, rna_paths, 0);
|
||||
keyed_pose_bones.add(pchan);
|
||||
};
|
||||
bke::BKE_action_find_fcurves_with_bones(act, slot.handle, autokey_pose_bones);
|
||||
}
|
||||
|
||||
/* send notifiers for this */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr);
|
||||
}
|
||||
|
||||
/* Apply the relevant changes to the pose */
|
||||
static void poselib_blend_apply(bContext *C, wmOperator *op)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
|
||||
if (!pbd->needs_redraw) {
|
||||
return;
|
||||
}
|
||||
pbd->needs_redraw = false;
|
||||
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
|
||||
/* The pose needs updating, whether it's for restoring the original pose or for showing the
|
||||
* result of the blend. */
|
||||
for (Object *ob : pbd->objects) {
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
}
|
||||
|
||||
if (pbd->state != POSE_BLEND_BLENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perform the actual blending. */
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
|
||||
animrig::Action &pose_action = poselib_action_to_blend(pbd)->wrap();
|
||||
if (pose_action.slot_array_num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
animrig::pose_apply_action(pbd->objects, pose_action, &anim_eval_context, pbd->blend_factor);
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
|
||||
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
|
||||
{
|
||||
pbd->blend_factor = new_factor;
|
||||
pbd->needs_redraw = true;
|
||||
}
|
||||
|
||||
static void poselib_toggle_flipped(PoseBlendData *pbd)
|
||||
{
|
||||
/* The pose will toggle between flipped and normal. This means the pose
|
||||
* backup has to change, as it only contains the bones for one side. */
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
|
||||
pbd->is_flipped = !pbd->is_flipped;
|
||||
pbd->needs_redraw = true;
|
||||
|
||||
poselib_backup_posecopy(pbd);
|
||||
}
|
||||
|
||||
/* Return operator return value. */
|
||||
static wmOperatorStatus poselib_blend_handle_event(bContext * /*C*/,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
|
||||
ED_slider_modal(pbd->slider, event);
|
||||
const float factor = ED_slider_factor_get(pbd->slider);
|
||||
poselib_blend_set_factor(pbd, factor);
|
||||
|
||||
if (event->type == MOUSEMOVE) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Handle the release confirm event directly, it has priority over others. */
|
||||
if (pbd->release_confirm_info.use_release_confirm &&
|
||||
(event->type == pbd->release_confirm_info.init_event_type) && (event->val == KM_RELEASE))
|
||||
{
|
||||
pbd->state = POSE_BLEND_CONFIRM;
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Ctrl manages the 'flipped' state. It works as a toggle so if the operator started in flipped
|
||||
* mode, pressing it will unflip the pose. */
|
||||
if (ELEM(event->val, KM_PRESS, KM_RELEASE) &&
|
||||
ELEM(event->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY))
|
||||
{
|
||||
poselib_toggle_flipped(pbd);
|
||||
}
|
||||
|
||||
/* only accept 'press' event, and ignore 'release', so that we don't get double actions */
|
||||
if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* NORMAL EVENT HANDLING... */
|
||||
/* searching takes priority over normal activity */
|
||||
switch (event->type) {
|
||||
/* Exit - cancel. */
|
||||
case EVT_ESCKEY:
|
||||
case RIGHTMOUSE:
|
||||
pbd->state = POSE_BLEND_CANCEL;
|
||||
break;
|
||||
|
||||
/* Exit - confirm. */
|
||||
case LEFTMOUSE:
|
||||
case EVT_RETKEY:
|
||||
case EVT_PADENTER:
|
||||
case EVT_SPACEKEY:
|
||||
pbd->state = POSE_BLEND_CONFIRM;
|
||||
break;
|
||||
|
||||
/* TODO(Sybren): toggle between original pose and poselib pose. */
|
||||
case EVT_TABKEY:
|
||||
pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING;
|
||||
pbd->needs_redraw = true;
|
||||
break;
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
|
||||
static Vector<Object *> get_poselib_objects(bContext &C)
|
||||
{
|
||||
Vector<PointerRNA> selected_objects;
|
||||
CTX_data_selected_objects(&C, &selected_objects);
|
||||
|
||||
Vector<Object *> selected_pose_objects;
|
||||
for (const PointerRNA &ptr : selected_objects) {
|
||||
Object *object = reinterpret_cast<Object *>(ptr.owner_id);
|
||||
if (!object || !object->pose) {
|
||||
continue;
|
||||
}
|
||||
selected_pose_objects.append(object);
|
||||
}
|
||||
|
||||
Object *active_object = CTX_data_active_object(&C);
|
||||
/* The active object may not be selected, it should be added because you can still switch to pose
|
||||
* mode. */
|
||||
if (active_object && active_object->pose && !selected_pose_objects.contains(active_object)) {
|
||||
selected_pose_objects.append(active_object);
|
||||
}
|
||||
return selected_pose_objects;
|
||||
}
|
||||
|
||||
static void poselib_tempload_exit(PoseBlendData *pbd)
|
||||
{
|
||||
using namespace blender::ed;
|
||||
asset::temp_id_consumer_free(&pbd->temp_id_consumer);
|
||||
}
|
||||
|
||||
static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender::ed;
|
||||
|
||||
const asset_system::AssetRepresentation *asset = nullptr;
|
||||
|
||||
if (asset::operator_asset_reference_props_is_set(*op->ptr)) {
|
||||
asset = asset::operator_asset_reference_props_get_asset_from_all_library(
|
||||
*C, *op->ptr, op->reports);
|
||||
if (!asset) {
|
||||
/* Explicit asset reference passed, but cannot be found. Error out. */
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Asset not found: '%s'",
|
||||
RNA_string_get(op->ptr, "relative_asset_identifier").c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If no explicit asset reference was passed, get asset from context. */
|
||||
asset = CTX_wm_asset(C);
|
||||
if (!asset) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No asset in context");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->get_id_type() != ID_AC) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Asset ('%s') is not an action data-block",
|
||||
asset->get_name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (asset->is_online_only()) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Pose '%s' needs downloading before it can be applied (check context menu)",
|
||||
asset->get_name().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
|
||||
pbd->temp_id_consumer = asset::temp_id_consumer_create(asset);
|
||||
return reinterpret_cast<bAction *>(asset::temp_id_consumer_ensure_local_id(
|
||||
pbd->temp_id_consumer, ID_AC, CTX_data_main(C), op->reports));
|
||||
}
|
||||
|
||||
static bAction *flip_pose(bContext *C, Span<Object *> objects, bAction *action)
|
||||
{
|
||||
bAction *action_copy = reinterpret_cast<bAction *>(
|
||||
BKE_id_copy_ex(nullptr, &action->id, nullptr, LIB_ID_COPY_LOCALIZE));
|
||||
|
||||
/* Lock the window manager while flipping the pose. Flipping requires temporarily modifying the
|
||||
* pose, which can cause unwanted visual glitches. */
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
const bool interface_was_locked = CTX_wm_interface_locked(C);
|
||||
WM_locked_interface_set(wm, true);
|
||||
|
||||
BKE_action_flip_with_pose(action_copy, objects);
|
||||
|
||||
WM_locked_interface_set(wm, interface_was_locked);
|
||||
return action_copy;
|
||||
}
|
||||
|
||||
/* Return true on success, false if the context isn't suitable. */
|
||||
static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
op->customdata = nullptr;
|
||||
|
||||
/* check if valid poselib */
|
||||
Vector<Object *> selected_pose_objects = get_poselib_objects(*C);
|
||||
if (selected_pose_objects.is_empty()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up blend state info. */
|
||||
PoseBlendData *pbd;
|
||||
op->customdata = pbd = MEM_new<PoseBlendData>("PoseLib Preview Data");
|
||||
|
||||
pbd->act = poselib_blend_init_get_action(C, op);
|
||||
if (pbd->act == nullptr) {
|
||||
/* No report here. The poll function cannot check if the operator properties have an asset
|
||||
* reference to determine the asset to operate on, in which case we fallback to getting the
|
||||
* asset from context. */
|
||||
return false;
|
||||
}
|
||||
if (pbd->act->wrap().slots().size() == 0) {
|
||||
BKE_report(op->reports, RPT_ERROR, "This pose asset is empty, and thus has no pose");
|
||||
return false;
|
||||
}
|
||||
|
||||
pbd->is_flipped = RNA_struct_property_is_set(op->ptr, "flipped") ?
|
||||
RNA_boolean_get(op->ptr, "flipped") :
|
||||
(event && (event->modifier & KM_CTRL));
|
||||
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
|
||||
|
||||
/* Only construct the flipped pose if there is a chance it's actually needed. */
|
||||
const bool is_interactive = (event != nullptr);
|
||||
if (is_interactive || pbd->is_flipped) {
|
||||
pbd->act_flipped = flip_pose(C, selected_pose_objects, pbd->act);
|
||||
}
|
||||
|
||||
/* Get the basic data. */
|
||||
pbd->objects = selected_pose_objects;
|
||||
|
||||
pbd->scene = CTX_data_scene(C);
|
||||
pbd->area = CTX_wm_area(C);
|
||||
|
||||
pbd->state = POSE_BLEND_INIT;
|
||||
pbd->needs_redraw = true;
|
||||
|
||||
/* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */
|
||||
pbd->release_confirm_info.use_release_confirm = false;
|
||||
|
||||
/* Release confirm data. Only available if there's an event to work with. */
|
||||
if (is_interactive) {
|
||||
PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm");
|
||||
if (release_confirm_prop && RNA_property_is_set(op->ptr, release_confirm_prop)) {
|
||||
pbd->release_confirm_info.use_release_confirm = RNA_property_boolean_get(
|
||||
op->ptr, release_confirm_prop);
|
||||
}
|
||||
else {
|
||||
pbd->release_confirm_info.use_release_confirm = event->val != KM_RELEASE;
|
||||
}
|
||||
|
||||
pbd->slider = ED_slider_create(C);
|
||||
ED_slider_init(pbd->slider, event);
|
||||
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
|
||||
ED_slider_allow_overshoot_set(pbd->slider, true, true);
|
||||
ED_slider_allow_increments_set(pbd->slider, false);
|
||||
ED_slider_factor_bounds_set(pbd->slider, -1, 1);
|
||||
}
|
||||
|
||||
if (pbd->release_confirm_info.use_release_confirm) {
|
||||
BLI_assert(is_interactive);
|
||||
pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type(
|
||||
event->type);
|
||||
}
|
||||
|
||||
/* Make backups for blending and restoring the pose. */
|
||||
poselib_backup_posecopy(pbd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void poselib_blend_cleanup(bContext *C, wmOperator *op)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
/* Redraw the header so that it doesn't show any of our stuff anymore. */
|
||||
ED_area_status_text(pbd->area, nullptr);
|
||||
ED_workspace_status_text(C, nullptr);
|
||||
|
||||
if (pbd->slider) {
|
||||
ED_slider_destroy(C, pbd->slider);
|
||||
}
|
||||
|
||||
switch (pbd->state) {
|
||||
case POSE_BLEND_CONFIRM: {
|
||||
Scene *scene = pbd->scene;
|
||||
poselib_keytag_pose(C, scene, pbd);
|
||||
|
||||
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
|
||||
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
|
||||
RNA_boolean_set(op->ptr, "flipped", pbd->is_flipped);
|
||||
break;
|
||||
}
|
||||
|
||||
case POSE_BLEND_INIT:
|
||||
case POSE_BLEND_BLENDING:
|
||||
case POSE_BLEND_ORIGINAL:
|
||||
/* Cleanup should not be called directly from these states. */
|
||||
BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state");
|
||||
BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator");
|
||||
ATTR_FALLTHROUGH;
|
||||
case POSE_BLEND_CANCEL:
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
break;
|
||||
}
|
||||
|
||||
for (Object *ob : pbd->objects) {
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
}
|
||||
/* Update mouse-hover highlights. */
|
||||
WM_event_add_mousemove(win);
|
||||
}
|
||||
|
||||
static void poselib_blend_free(wmOperator *op)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
if (pbd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pbd->act_flipped) {
|
||||
BKE_id_free(nullptr, pbd->act_flipped);
|
||||
}
|
||||
poselib_tempload_exit(pbd);
|
||||
|
||||
/* Free temp data for operator */
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
pbd->pose_backup = nullptr;
|
||||
|
||||
MEM_delete(pbd);
|
||||
}
|
||||
|
||||
static wmOperatorStatus poselib_blend_exit(bContext *C, wmOperator *op)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
const ePoseBlendState exit_state = pbd->state;
|
||||
|
||||
poselib_blend_cleanup(C, op);
|
||||
poselib_blend_free(op);
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WM_cursor_modal_restore(win);
|
||||
|
||||
if (exit_state == POSE_BLEND_CANCEL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* Cancel previewing operation (called when exiting Blender) */
|
||||
static void poselib_blend_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
pbd->state = POSE_BLEND_CANCEL;
|
||||
poselib_blend_exit(C, op);
|
||||
}
|
||||
|
||||
/* Main modal status check. */
|
||||
static wmOperatorStatus poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const wmOperatorStatus operator_result = poselib_blend_handle_event(C, op, event);
|
||||
|
||||
const PoseBlendData *pbd = static_cast<const PoseBlendData *>(op->customdata);
|
||||
if (ELEM(pbd->state, POSE_BLEND_CONFIRM, POSE_BLEND_CANCEL)) {
|
||||
return poselib_blend_exit(C, op);
|
||||
}
|
||||
|
||||
if (pbd->needs_redraw) {
|
||||
|
||||
WorkspaceStatus status(C);
|
||||
|
||||
if (pbd->state == POSE_BLEND_BLENDING) {
|
||||
status.item(IFACE_("Show Original Pose"), ICON_EVENT_TAB);
|
||||
}
|
||||
else {
|
||||
status.item(IFACE_("Show Blended Pose"), ICON_EVENT_TAB);
|
||||
}
|
||||
|
||||
ED_slider_status_get(pbd->slider, status);
|
||||
|
||||
status.item_bool(IFACE_("Flip Pose"), pbd->is_flipped, ICON_EVENT_CTRL);
|
||||
|
||||
poselib_blend_apply(C, op);
|
||||
}
|
||||
|
||||
return operator_result;
|
||||
}
|
||||
|
||||
static wmOperatorStatus poselib_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (!poselib_blend_init_data(C, op, event)) {
|
||||
poselib_blend_free(op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
poselib_blend_apply(C, op);
|
||||
|
||||
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
|
||||
pbd->state = POSE_BLEND_CONFIRM;
|
||||
return poselib_blend_exit(C, op);
|
||||
}
|
||||
|
||||
static wmOperatorStatus poselib_apply_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return poselib_apply_invoke(C, op, nullptr);
|
||||
}
|
||||
|
||||
/* Modal Operator init. */
|
||||
static wmOperatorStatus poselib_blend_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (!poselib_blend_init_data(C, op, event)) {
|
||||
poselib_blend_free(op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
|
||||
|
||||
/* Do initial apply to have something to look at. */
|
||||
poselib_blend_apply(C, op);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Single-shot apply. */
|
||||
static wmOperatorStatus poselib_blend_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return poselib_apply_invoke(C, op, nullptr);
|
||||
}
|
||||
|
||||
/* Poll callback for operators that require existing PoseLib data (with poses) to work. */
|
||||
static bool poselib_blend_poll(bContext *C)
|
||||
{
|
||||
Span<Object *> selected_pose_objects = get_poselib_objects(*C);
|
||||
if (selected_pose_objects.is_empty()) {
|
||||
/* Pose lib is only for armatures in pose mode. */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Operator properties can set an asset reference to determine the asset to operate on (the pose
|
||||
* can then be applied via shortcut too, for example). If this isn't set, an active asset from
|
||||
* context is queried. */
|
||||
void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* Identifiers: */
|
||||
ot->name = "Apply Pose Asset";
|
||||
ot->idname = "POSELIB_OT_apply_pose_asset";
|
||||
ot->description = "Apply the given Pose Action to the rig";
|
||||
|
||||
/* Callbacks: */
|
||||
ot->invoke = poselib_apply_invoke;
|
||||
ot->exec = poselib_apply_exec;
|
||||
ot->poll = poselib_blend_poll;
|
||||
|
||||
/* Flags: */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* Properties: */
|
||||
ed::asset::operator_asset_reference_props_register(*ot->srna);
|
||||
RNA_def_float_factor(ot->srna,
|
||||
"blend_factor",
|
||||
1.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A negative "
|
||||
"value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/* See comment on #POSELIB_OT_apply_pose_asset. */
|
||||
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* Identifiers: */
|
||||
ot->name = "Blend Pose Asset";
|
||||
ot->idname = "POSELIB_OT_blend_pose_asset";
|
||||
ot->description = "Blend the given Pose Action to the rig";
|
||||
|
||||
/* Callbacks: */
|
||||
ot->invoke = poselib_blend_invoke;
|
||||
ot->modal = poselib_blend_modal;
|
||||
ot->cancel = poselib_blend_cancel;
|
||||
ot->exec = poselib_blend_exec;
|
||||
ot->poll = poselib_blend_poll;
|
||||
|
||||
/* Flags: */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
|
||||
|
||||
/* Properties: */
|
||||
ed::asset::operator_asset_reference_props_register(*ot->srna);
|
||||
prop = RNA_def_float_factor(ot->srna,
|
||||
"blend_factor",
|
||||
0.0f,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A "
|
||||
"negative value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
|
||||
* property just exists for symmetry with the Apply operator (and thus simplicity of the rest of
|
||||
* the code, which can assume this property exists). */
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"release_confirm",
|
||||
false,
|
||||
"Confirm on Release",
|
||||
"Always confirm operation when releasing button");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1492
blender-5.2.0/source/blender/editors/armature/pose_select.cc
Normal file
1492
blender-5.2.0/source/blender/editors/armature/pose_select.cc
Normal file
File diff suppressed because it is too large
Load Diff
1799
blender-5.2.0/source/blender/editors/armature/pose_slide.cc
Normal file
1799
blender-5.2.0/source/blender/editors/armature/pose_slide.cc
Normal file
File diff suppressed because it is too large
Load Diff
1536
blender-5.2.0/source/blender/editors/armature/pose_transform.cc
Normal file
1536
blender-5.2.0/source/blender/editors/armature/pose_transform.cc
Normal file
File diff suppressed because it is too large
Load Diff
542
blender-5.2.0/source/blender/editors/armature/pose_utils.cc
Normal file
542
blender-5.2.0/source/blender/editors/armature/pose_utils.cc
Normal file
@@ -0,0 +1,542 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_path.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_anim_api.hh"
|
||||
#include "ED_anim_transformable.hh"
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_keyframing.hh"
|
||||
#include "ED_object.hh"
|
||||
|
||||
#include "ANIM_action.hh"
|
||||
#include "ANIM_action_iterators.hh"
|
||||
#include "ANIM_keyframing.hh"
|
||||
#include "ANIM_keyingsets.hh"
|
||||
#include "ANIM_rna.hh"
|
||||
|
||||
#include "armature_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* *********************************************** */
|
||||
/* Contents of this File:
|
||||
*
|
||||
* This file contains methods shared between Pose Slide and Pose Lib;
|
||||
* primarily the functions in question concern Animato <-> Pose
|
||||
* convenience functions, such as applying/getting pose values
|
||||
* and/or inserting keyframes for these.
|
||||
*/
|
||||
/* *********************************************** */
|
||||
/* FCurves <-> PoseChannels Links */
|
||||
|
||||
/**
|
||||
* Fills the `r_curves` vector with curves used by the given `ptr`.
|
||||
* The returned flags indicate which properties are animated.
|
||||
*/
|
||||
static eAction_TransformFlags get_item_transform_flags_and_fcurves(ID &id,
|
||||
PointerRNA &ptr,
|
||||
Vector<FCurve *> &r_curves)
|
||||
{
|
||||
AnimData *adt = BKE_animdata_from_id(&id);
|
||||
if (!adt || !adt->action) {
|
||||
return eAction_TransformFlags(0);
|
||||
}
|
||||
animrig::Action &action = adt->action->wrap();
|
||||
|
||||
short flags = 0;
|
||||
|
||||
/* Get the basic path to the properties of interest. */
|
||||
const std::optional<std::string> path_to_struct = RNA_path_from_ID_to_struct(&ptr);
|
||||
StringRef base_path;
|
||||
if (RNA_struct_is_ID(ptr.type)) {
|
||||
base_path = "";
|
||||
}
|
||||
else {
|
||||
if (!path_to_struct.has_value()) {
|
||||
BLI_assert_unreachable();
|
||||
return eAction_TransformFlags(0);
|
||||
}
|
||||
base_path = path_to_struct.value();
|
||||
}
|
||||
|
||||
animrig::foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
|
||||
if (fcurve.rna_path == nullptr) {
|
||||
return;
|
||||
}
|
||||
StringRefNull fcurve_path(fcurve.rna_path);
|
||||
|
||||
if (!base_path.is_empty() && !fcurve_path.startswith(base_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringRef property_name;
|
||||
if (base_path.is_empty()) {
|
||||
property_name = fcurve_path;
|
||||
}
|
||||
else {
|
||||
/* Normal properties are separated by a dot, custom properties don't have that. */
|
||||
if (fcurve_path[base_path.size()] == '.') {
|
||||
property_name = fcurve_path.substr(base_path.size() + 1);
|
||||
}
|
||||
else {
|
||||
property_name = fcurve_path.substr(base_path.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (property_name == "location") {
|
||||
flags |= ACT_TRANS_LOC;
|
||||
r_curves.append(&fcurve);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_name == "scale") {
|
||||
flags |= ACT_TRANS_SCALE;
|
||||
r_curves.append(&fcurve);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_name == "rotation_euler" || property_name == "rotation_quaternion" ||
|
||||
property_name == "rotation_axis_angle")
|
||||
{
|
||||
flags |= ACT_TRANS_ROT;
|
||||
r_curves.append(&fcurve);
|
||||
return;
|
||||
}
|
||||
|
||||
if (property_name.startswith("bbone_")) {
|
||||
flags |= ACT_TRANS_BBONE;
|
||||
r_curves.append(&fcurve);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Custom properties only. */
|
||||
if (property_name.startswith("[\"")) {
|
||||
flags |= ACT_TRANS_PROP;
|
||||
r_curves.append(&fcurve);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
/* return flags found */
|
||||
return eAction_TransformFlags(flags);
|
||||
}
|
||||
/**
|
||||
* Stores a `PropertySnapshot` of the property with the given `property_name` in the given Vector.
|
||||
* If the property does not exist in the `ptr` the function doesn't do anything. Also the property
|
||||
* has to be supported by `ed::rna_property_get_as_float`.
|
||||
*/
|
||||
static void store_property_snapshot(PointerRNA &ptr,
|
||||
const StringRef property_name,
|
||||
Vector<PropertySnapshot> &snapshots)
|
||||
{
|
||||
|
||||
PropertyRNA *prop = RNA_struct_find_property(&ptr, property_name.data());
|
||||
if (!prop) {
|
||||
return;
|
||||
}
|
||||
Array<float> property_values = animrig::rna_property_get_as_float(ptr, *prop);
|
||||
if (property_values.size() == 0) {
|
||||
/* Unsupported property type. */
|
||||
return;
|
||||
}
|
||||
snapshots.append({prop, std::move(property_values)});
|
||||
}
|
||||
|
||||
static void store_starting_transform(SlideSubject &slide_subject,
|
||||
ed::AnimTransformable &transformable)
|
||||
{
|
||||
slide_subject.old_loc = transformable.get_property(
|
||||
ed::AnimTransformable::PropertyType::LOCATION);
|
||||
slide_subject.old_rot = transformable.get_rotation();
|
||||
slide_subject.old_scale = transformable.get_property(ed::AnimTransformable::PropertyType::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* `id_properties` and `system_properties` can be a nullptr and are skipped if they are.
|
||||
*/
|
||||
static void store_id_properties(SlideSubject &slide_subject,
|
||||
PointerRNA &ptr,
|
||||
IDProperty *id_properties,
|
||||
IDProperty *system_properties)
|
||||
{
|
||||
if (id_properties) {
|
||||
for (const IDProperty &id_prop : id_properties->data.group) {
|
||||
char name_escaped[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(name_escaped, id_prop.name, sizeof(name_escaped));
|
||||
std::string property_name_with_brackets = fmt::format("[\"{}\"]", name_escaped);
|
||||
store_property_snapshot(ptr, property_name_with_brackets, slide_subject.properties);
|
||||
}
|
||||
}
|
||||
if (system_properties) {
|
||||
for (const IDProperty &id_prop : system_properties->data.group) {
|
||||
store_property_snapshot(ptr, id_prop.name, slide_subject.system_properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* helper for slide_subjects_get() -> get the relevant F-Curves per PoseChannel */
|
||||
static void pchan_to_slide_subject(ListBaseT<SlideSubject> &slide_subjects,
|
||||
Object &ob,
|
||||
bPoseChannel &pchan)
|
||||
{
|
||||
PointerRNA bone_ptr = RNA_pointer_create_discrete(&ob.id, RNA_PoseBone, &pchan);
|
||||
Vector<FCurve *> curves;
|
||||
const eAction_TransformFlags transFlags = get_item_transform_flags_and_fcurves(
|
||||
ob.id, bone_ptr, curves);
|
||||
|
||||
if (!transFlags) {
|
||||
return;
|
||||
}
|
||||
|
||||
SlideSubject *slide_subject = MEM_new<SlideSubject>("SlideSubject");
|
||||
BLI_addtail(&slide_subjects, slide_subject);
|
||||
slide_subject->fcurves = curves;
|
||||
|
||||
ed::AnimTransformable *transformable = MEM_new<ed::AnimTransformable>(
|
||||
"transformable_pose_bone", ob, pchan);
|
||||
slide_subject->transformable = transformable;
|
||||
|
||||
/* Set pchan's transform flags. */
|
||||
slide_subject->transform_flag = transFlags;
|
||||
|
||||
store_starting_transform(*slide_subject, *transformable);
|
||||
|
||||
slide_subject->ptr = bone_ptr;
|
||||
|
||||
if (transFlags & ACT_TRANS_BBONE) {
|
||||
store_property_snapshot(bone_ptr, "bbone_rollin", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_rollout", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_curveinx", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_curveoutx", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_curveinz", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_curveoutz", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_easein", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_easeout", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_scalein", slide_subject->additional_properties);
|
||||
store_property_snapshot(bone_ptr, "bbone_scaleout", slide_subject->additional_properties);
|
||||
}
|
||||
|
||||
/* Make copy of custom properties. */
|
||||
if (transFlags & ACT_TRANS_PROP) {
|
||||
store_id_properties(*slide_subject, bone_ptr, pchan.prop, pchan.system_properties);
|
||||
}
|
||||
}
|
||||
|
||||
static Object *animated_armature_ob_get(Object *ob_)
|
||||
{
|
||||
Object *ob = BKE_object_pose_armature_get(ob_);
|
||||
if (!ELEM(nullptr, ob, ob->data, ob->adt, ob->adt->action)) {
|
||||
return ob;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void get_pose_bones_for_slide(bContext *C, ListBaseT<SlideSubject> &slide_subjects)
|
||||
{
|
||||
/* For each Pose-Channel which gets affected, get the F-Curves for that channel
|
||||
* and set the relevant transform flags... */
|
||||
Object *prev_ob, *ob_pose_armature;
|
||||
|
||||
prev_ob = nullptr;
|
||||
ob_pose_armature = nullptr;
|
||||
/* Used to avoid duplicates when using mirroring. */
|
||||
Set<bPoseChannel *> inserted_bones;
|
||||
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
|
||||
BLI_assert(pchan != nullptr);
|
||||
if (ob != prev_ob) {
|
||||
prev_ob = ob;
|
||||
ob_pose_armature = animated_armature_ob_get(ob);
|
||||
}
|
||||
|
||||
if (ob_pose_armature == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
|
||||
/* No action means no FCurves. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inserted_bones.add(pchan)) {
|
||||
continue;
|
||||
}
|
||||
pchan_to_slide_subject(slide_subjects, *ob_pose_armature, *pchan);
|
||||
|
||||
if (ob_pose_armature->pose->flag & POSE_MIRROR_EDIT) {
|
||||
bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name);
|
||||
if (pchan_mirror && inserted_bones.add(pchan_mirror)) {
|
||||
pchan_to_slide_subject(slide_subjects, *ob_pose_armature, *pchan_mirror);
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* If no PoseChannels were found, try a second pass, doing visible ones instead.
|
||||
* i.e. if nothing selected, do whole pose.
|
||||
*/
|
||||
if (slide_subjects.is_empty()) {
|
||||
prev_ob = nullptr;
|
||||
ob_pose_armature = nullptr;
|
||||
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
|
||||
BLI_assert(pchan != nullptr);
|
||||
if (ob != prev_ob) {
|
||||
prev_ob = ob;
|
||||
ob_pose_armature = animated_armature_ob_get(ob);
|
||||
}
|
||||
|
||||
if (ob_pose_armature == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
|
||||
/* No action means no FCurves. */
|
||||
continue;
|
||||
}
|
||||
|
||||
pchan_to_slide_subject(slide_subjects, *ob_pose_armature, *pchan);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_objects_for_slide(bContext *C, ListBaseT<SlideSubject> &slider_data)
|
||||
{
|
||||
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
|
||||
PointerRNA object_ptr = RNA_pointer_create_discrete(&ob->id, RNA_Object, ob);
|
||||
|
||||
Vector<FCurve *> curves;
|
||||
const eAction_TransformFlags transFlags = get_item_transform_flags_and_fcurves(
|
||||
ob->id, object_ptr, curves);
|
||||
|
||||
if (!transFlags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SlideSubject *slide_subject = MEM_new<SlideSubject>("TransformableFCurveLink");
|
||||
BLI_addtail(&slider_data, slide_subject);
|
||||
slide_subject->fcurves = curves;
|
||||
|
||||
ed::AnimTransformable *transformable = MEM_new<ed::AnimTransformable>("transformable_object",
|
||||
*ob);
|
||||
slide_subject->transformable = transformable;
|
||||
slide_subject->transform_flag = transFlags;
|
||||
|
||||
store_starting_transform(*slide_subject, *transformable);
|
||||
slide_subject->ptr = object_ptr;
|
||||
|
||||
if (transFlags & ACT_TRANS_PROP) {
|
||||
store_id_properties(*slide_subject, object_ptr, ob->id.properties, ob->id.system_properties);
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
void slide_subjects_get(bContext *C, ListBaseT<SlideSubject> *r_transformable_list)
|
||||
{
|
||||
BLI_assert(r_transformable_list != nullptr);
|
||||
const eContextObjectMode mode = CTX_data_mode_enum(C);
|
||||
switch (mode) {
|
||||
case CTX_MODE_POSE:
|
||||
case CTX_MODE_PAINT_WEIGHT:
|
||||
get_pose_bones_for_slide(C, *r_transformable_list);
|
||||
break;
|
||||
case CTX_MODE_OBJECT:
|
||||
get_objects_for_slide(C, *r_transformable_list);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Not implemented. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void slide_subjects_free(ListBaseT<SlideSubject> *slide_subjects)
|
||||
{
|
||||
SlideSubject *slide_subject, *pfln = nullptr;
|
||||
|
||||
/* free the temp pchan links and their data */
|
||||
for (slide_subject = static_cast<SlideSubject *>(slide_subjects->first); slide_subject;
|
||||
slide_subject = pfln)
|
||||
{
|
||||
pfln = slide_subject->next;
|
||||
|
||||
MEM_delete(slide_subject->transformable);
|
||||
|
||||
/* We cannot use BLI_freelinkN because that casts the SlideSubject to a C-style
|
||||
* struct causing MEM_delete to do a C-style delete and not deallocate the Vector. */
|
||||
BLI_remlink(slide_subjects, slide_subject);
|
||||
MEM_delete(slide_subject);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------- */
|
||||
|
||||
void slide_subjects_refresh(bContext *C, const SlideSubject &slide_subject)
|
||||
{
|
||||
ID *id = slide_subject.ptr.owner_id;
|
||||
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
|
||||
DEG_id_tag_update(id, ID_RECALC_TRANSFORM);
|
||||
switch (slide_subject.transformable->type()) {
|
||||
case ed::AnimTransformable::Type::POSE_BONE:
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, id_cast<Object *>(id));
|
||||
break;
|
||||
case ed::AnimTransformable::Type::OBJECT:
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, id_cast<Object *>(id));
|
||||
break;
|
||||
}
|
||||
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
if (adt && adt->action) {
|
||||
DEG_id_tag_update(&adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
|
||||
}
|
||||
}
|
||||
|
||||
void slide_subjects_reset(ListBaseT<SlideSubject> *slide_subjects)
|
||||
{
|
||||
/* Iterate over each transformable affected, restoring all channels to their original values. */
|
||||
for (SlideSubject &slide_subject : *slide_subjects) {
|
||||
ed::AnimTransformable *transformable = slide_subject.transformable;
|
||||
|
||||
/* just copy all the values over regardless of whether they changed or not */
|
||||
transformable->set_property(ed::AnimTransformable::PropertyType::LOCATION,
|
||||
slide_subject.old_loc,
|
||||
ed::AXIS_MUTABLE_ALL);
|
||||
transformable->set_rotation(slide_subject.old_rot);
|
||||
transformable->set_property(
|
||||
ed::AnimTransformable::PropertyType::SCALE, slide_subject.old_scale, ed::AXIS_MUTABLE_ALL);
|
||||
|
||||
for (PropertySnapshot &extra_prop : slide_subject.additional_properties) {
|
||||
animrig::rna_property_set_as_float(
|
||||
slide_subject.ptr, *extra_prop.property, extra_prop.values);
|
||||
}
|
||||
|
||||
for (PropertySnapshot &custom_prop : slide_subject.properties) {
|
||||
animrig::rna_property_set_as_float(
|
||||
slide_subject.ptr, *custom_prop.property, custom_prop.values);
|
||||
}
|
||||
for (PropertySnapshot &custom_prop : slide_subject.system_properties) {
|
||||
animrig::rna_property_set_as_float(
|
||||
slide_subject.ptr, *custom_prop.property, custom_prop.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void slide_subjects_autokey(bContext *C,
|
||||
Scene *scene,
|
||||
const ListBaseT<SlideSubject> *slide_subjects)
|
||||
{
|
||||
bool anything_to_key = false;
|
||||
for (SlideSubject &slide_subject : *slide_subjects) {
|
||||
if (!animrig::autokeyframe_cfra_can_key(scene, slide_subject.ptr.owner_id)) {
|
||||
continue;
|
||||
}
|
||||
anything_to_key = true;
|
||||
break;
|
||||
}
|
||||
/* If there is nothing to key, return before deselecting any keys. */
|
||||
if (!anything_to_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
ANIM_deselect_keys_in_animation_editors(C);
|
||||
|
||||
/* Insert keyframes as necessary if auto-key-framing. */
|
||||
for (SlideSubject &slide_subject : *slide_subjects) {
|
||||
PointerRNA &ptr = slide_subject.ptr;
|
||||
if (!animrig::autokeyframe_cfra_can_key(scene, slide_subject.ptr.owner_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<RNAPath> paths;
|
||||
/* The transform flags tell us which properties have keys. Properties without keys cannot pose
|
||||
* slide, so should not be auto keyed. */
|
||||
if (slide_subject.transform_flag & ACT_TRANS_LOC) {
|
||||
paths.append({"location"});
|
||||
}
|
||||
if (slide_subject.transform_flag & ACT_TRANS_ROT) {
|
||||
paths.append(
|
||||
{animrig::get_rotation_mode_path(slide_subject.transformable->get_rotation_mode())});
|
||||
}
|
||||
if (slide_subject.transform_flag & ACT_TRANS_SCALE) {
|
||||
paths.append({"scale"});
|
||||
}
|
||||
|
||||
/* No need to check the transform_flag here, because those vectors are only filled if the flag
|
||||
* was set in the first place.*/
|
||||
for (const PropertySnapshot &snapshot : slide_subject.additional_properties) {
|
||||
paths.append({RNA_property_identifier(snapshot.property)});
|
||||
}
|
||||
for (const PropertySnapshot &snapshot : slide_subject.properties) {
|
||||
char name_escaped[MAX_IDPROP_NAME * 2];
|
||||
BLI_str_escape(
|
||||
name_escaped, RNA_property_identifier(snapshot.property), sizeof(name_escaped));
|
||||
paths.append({fmt::format("[\"{}\"]", name_escaped)});
|
||||
}
|
||||
for (const PropertySnapshot &snapshot : slide_subject.system_properties) {
|
||||
paths.append({RNA_property_identifier(snapshot.property)});
|
||||
}
|
||||
|
||||
switch (slide_subject.transformable->type()) {
|
||||
case ed::AnimTransformable::Type::POSE_BONE:
|
||||
animrig::autokeyframe_pose_channel(C,
|
||||
scene,
|
||||
id_cast<Object *>(ptr.owner_id),
|
||||
static_cast<bPoseChannel *>(ptr.data),
|
||||
paths,
|
||||
false);
|
||||
break;
|
||||
case ed::AnimTransformable::Type::OBJECT:
|
||||
animrig::autokeyframe_object(C, scene, id_cast<Object *>(ptr.owner_id), paths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Object *> objects;
|
||||
for (SlideSubject &slide_subject : *slide_subjects) {
|
||||
ID *owner_id = slide_subject.transformable->owner_id();
|
||||
if (GS(owner_id->name) != ID_OB) {
|
||||
continue;
|
||||
}
|
||||
objects.append(id_cast<Object *>(owner_id));
|
||||
}
|
||||
/* This includes all motion paths for bones. Could be more fine grained in the future to avoid
|
||||
* needless updates to data that was not changed. */
|
||||
ed::object::motion_paths_recalc(C, scene, ANIMVIZ_CALC_RANGE_CHANGED, objects);
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, nullptr);
|
||||
}
|
||||
|
||||
/* *********************************************** */
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user