Anim: Place Pose Bone Gizmo at Custom Transform
This PR adds an option "Affect Gizmo" for custom shape transforms to affect the transform gizmos. If enabled, this will place the gizmo at the location and orientation of the "Override Transform" (i.e. in its local space). For Orientations: The gizmo mode *is* respected. I.e. global mode is aligned to the world etc. There is a special case for "Gimbal" where it will not follow the orientation of the "Override Transform" bone. I think it makes sense to keep it that way since this is about the channels of the bone you are actually manipulating. The other option is "Use as Pivot" with which the bone is actually rotated around the override bone. This can be useful for rigs in which shapekeys and armature deformation is combined Taken over from #136468 For design task #135429 Co-authored-by: Wayde Moss <wbmoss_dev@yahoo.com> Pull Request: https://projects.blender.org/blender/blender/pulls/142847
This commit is contained in:
committed by
Christoph Lendenfeld
parent
8e992a15e8
commit
b2653be057
@@ -418,6 +418,12 @@ class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel):
|
||||
sub.prop(pchan, "custom_shape_scale_xyz", text="Scale")
|
||||
|
||||
sub.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text="Override Transform")
|
||||
subsub = sub.column()
|
||||
subsub.active = bool(pchan and pchan.custom_shape and pchan.custom_shape_transform)
|
||||
subsub.prop(pchan, "use_transform_at_custom_shape")
|
||||
subsubsub = subsub.column()
|
||||
subsubsub.active = subsub.active and pchan.use_transform_at_custom_shape
|
||||
subsubsub.prop(pchan, "use_transform_around_custom_shape")
|
||||
sub.prop(pchan, "use_custom_shape_bone_size")
|
||||
|
||||
sub.separator()
|
||||
|
||||
@@ -16,6 +16,7 @@ struct BlendDataReader;
|
||||
struct BlendLibReader;
|
||||
struct BlendWriter;
|
||||
struct bArmature;
|
||||
struct BoneParentTransform;
|
||||
|
||||
/* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */
|
||||
struct AnimationEvalContext;
|
||||
@@ -308,6 +309,21 @@ void BKE_pose_itasc_init(bItasc *itasc);
|
||||
*/
|
||||
bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan);
|
||||
|
||||
/**
|
||||
* Get the transform location, accounting for POSE_TRANSFORM_AT_CUSTOM_TX.
|
||||
*/
|
||||
void BKE_pose_channel_transform_location(const bArmature *arm,
|
||||
const bPoseChannel *pose_bone,
|
||||
float r_pose_space_pivot[3]);
|
||||
|
||||
/**
|
||||
* Get the transform pose orientation, accounting for
|
||||
* POSE_TRANSFORM_AT_CUSTOM_TX.
|
||||
*/
|
||||
void BKE_pose_channel_transform_orientation(const bArmature *arm,
|
||||
const bPoseChannel *pose_bone,
|
||||
float r_pose_orientation[3][3]);
|
||||
|
||||
/* Bone Groups API --------------------- */
|
||||
|
||||
/**
|
||||
|
||||
@@ -1398,6 +1398,46 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
|
||||
return pose_channel_in_IK_chain(ob, pchan, 0);
|
||||
}
|
||||
|
||||
static bool transform_follows_custom_tx(const bArmature *arm, const bPoseChannel *pchan)
|
||||
{
|
||||
if (arm->flag & ARM_NO_CUSTOM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pchan->custom || !pchan->custom_tx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pchan->flag & POSE_TRANSFORM_AT_CUSTOM_TX;
|
||||
}
|
||||
|
||||
void BKE_pose_channel_transform_orientation(const bArmature *arm,
|
||||
const bPoseChannel *pose_bone,
|
||||
float r_pose_orientation[3][3])
|
||||
{
|
||||
if (!transform_follows_custom_tx(arm, pose_bone)) {
|
||||
copy_m3_m4(r_pose_orientation, pose_bone->pose_mat);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(pose_bone->custom_tx);
|
||||
|
||||
const bPoseChannel *custom_tx_bone = pose_bone->custom_tx;
|
||||
copy_m3_m4(r_pose_orientation, custom_tx_bone->pose_mat);
|
||||
}
|
||||
|
||||
void BKE_pose_channel_transform_location(const bArmature *arm,
|
||||
const bPoseChannel *pose_bone,
|
||||
float r_pose_space_pivot[3])
|
||||
{
|
||||
if (!transform_follows_custom_tx(arm, pose_bone)) {
|
||||
copy_v3_v3(r_pose_space_pivot, pose_bone->pose_mat[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
copy_v3_v3(r_pose_space_pivot, pose_bone->custom_tx->pose_mat[3]);
|
||||
}
|
||||
|
||||
void BKE_pose_channels_hash_ensure(bPose *pose)
|
||||
{
|
||||
if (!pose->chanhash) {
|
||||
|
||||
@@ -126,7 +126,8 @@ bool calc_active_center_for_posemode(Object *ob, const bool select_only, float r
|
||||
{
|
||||
bPoseChannel *pchan = BKE_pose_channel_active_if_bonecoll_visible(ob);
|
||||
if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) {
|
||||
copy_v3_v3(r_center, pchan->pose_head);
|
||||
const bArmature *arm = static_cast<bArmature *>(ob->data);
|
||||
BKE_pose_channel_transform_location(arm, pchan, r_center);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -471,6 +471,12 @@ struct TransDataExtension {
|
||||
int rotOrder;
|
||||
/** Original object transformation used for rigid bodies. */
|
||||
float oloc[3], orot[3], oquat[4], orotAxis[3], orotAngle;
|
||||
|
||||
/**
|
||||
* Use when #TransDataBasic::center has been overridden but the real center is still needed
|
||||
* for internal calculations.
|
||||
*/
|
||||
float center_no_override[3];
|
||||
};
|
||||
|
||||
struct TransData2D {
|
||||
|
||||
@@ -395,10 +395,15 @@ static void add_pose_transdata(
|
||||
Bone *bone = pchan->bone;
|
||||
float pmat[3][3], omat[3][3];
|
||||
float cmat[3][3], tmat[3][3];
|
||||
float vec[3];
|
||||
|
||||
copy_v3_v3(vec, pchan->pose_mat[3]);
|
||||
copy_v3_v3(td->center, vec);
|
||||
const bArmature *arm = static_cast<bArmature *>(ob->data);
|
||||
BKE_pose_channel_transform_location(arm, pchan, td->center);
|
||||
if (pchan->flag & POSE_TRANSFORM_AROUND_CUSTOM_TX) {
|
||||
copy_v3_v3(td_ext->center_no_override, pchan->pose_mat[3]);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(td_ext->center_no_override, td->center);
|
||||
}
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
|
||||
@@ -449,11 +454,12 @@ static void add_pose_transdata(
|
||||
/* Proper way to get parent transform + our own transform + constraints transform. */
|
||||
copy_m3_m4(omat, ob->object_to_world().ptr());
|
||||
|
||||
/* New code, using "generic" BKE_bone_parent_transform_calc_from_pchan(). */
|
||||
{
|
||||
BoneParentTransform bpt;
|
||||
float rpmat[3][3];
|
||||
|
||||
/* Not using the pchan->custom_tx here because we need the transformation to be
|
||||
* relative to the actual bone being modified, not it's visual representation. */
|
||||
BKE_bone_parent_transform_calc_from_pchan(pchan, &bpt);
|
||||
if (t->mode == TFM_TRANSLATION) {
|
||||
copy_m3_m4(pmat, bpt.loc_mat);
|
||||
@@ -498,7 +504,7 @@ static void add_pose_transdata(
|
||||
}
|
||||
|
||||
/* For `axismtx` we use the bone's own transform. */
|
||||
copy_m3_m4(pmat, pchan->pose_mat);
|
||||
BKE_pose_channel_transform_orientation(arm, pchan, pmat);
|
||||
mul_m3_m3m3(td->axismtx, omat, pmat);
|
||||
normalize_m3(td->axismtx);
|
||||
|
||||
@@ -703,6 +709,9 @@ static void createTransPose(bContext * /*C*/, TransInfo *t)
|
||||
/* Use pose channels to fill trans data. */
|
||||
td = tc->data;
|
||||
tdx = tc->data_ext;
|
||||
tdx->center_no_override[0] = 0;
|
||||
tdx->center_no_override[1] = 0;
|
||||
tdx->center_no_override[2] = 0;
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
|
||||
if (pchan->bone->flag & BONE_TRANSFORM) {
|
||||
add_pose_transdata(t, pchan, ob, td++, tdx++);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_armature.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_crazyspace.hh"
|
||||
@@ -836,12 +837,16 @@ static int gizmo_3d_foreach_selected(const bContext *C,
|
||||
mul_m4_m4m4(mat_local, ob->world_to_object().ptr(), ob_iter->object_to_world().ptr());
|
||||
}
|
||||
|
||||
bArmature *arm = static_cast<bArmature *>(ob_iter->data);
|
||||
/* Use channels to get stats. */
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
|
||||
if (!(pchan->bone->flag & BONE_TRANSFORM)) {
|
||||
continue;
|
||||
}
|
||||
run_coord_with_matrix(pchan->pose_head, use_mat_local, mat_local);
|
||||
|
||||
float pchan_pivot[3];
|
||||
BKE_pose_channel_transform_location(arm, pchan, pchan_pivot);
|
||||
run_coord_with_matrix(pchan_pivot, use_mat_local, mat_local);
|
||||
totsel++;
|
||||
|
||||
if (r_drawflags) {
|
||||
|
||||
@@ -651,7 +651,7 @@ void ElementRotation_ex(const TransInfo *t,
|
||||
/* Extract and invert armature object matrix. */
|
||||
|
||||
if ((td->flag & TD_NO_LOC) == 0) {
|
||||
sub_v3_v3v3(vec, td->center, center);
|
||||
sub_v3_v3v3(vec, td_ext->center_no_override, center);
|
||||
|
||||
mul_m3_v3(tc->mat3, vec); /* To Global space. */
|
||||
mul_m3_v3(mat, vec); /* Applying rotation. */
|
||||
@@ -660,7 +660,8 @@ void ElementRotation_ex(const TransInfo *t,
|
||||
add_v3_v3(vec, center);
|
||||
/* `vec` now is the location where the object has to be. */
|
||||
|
||||
sub_v3_v3v3(vec, vec, td->center); /* Translation needed from the initial location. */
|
||||
/* Translation needed from the initial location. */
|
||||
sub_v3_v3v3(vec, vec, td_ext->center_no_override);
|
||||
|
||||
/* Special exception, see TD_PBONE_LOCAL_MTX definition comments. */
|
||||
if (td->flag & TD_PBONE_LOCAL_MTX_P) {
|
||||
|
||||
@@ -665,6 +665,7 @@ short calc_orientation_from_type_ex(const Scene *scene,
|
||||
if (ob) {
|
||||
if (ob->mode & OB_MODE_POSE) {
|
||||
const bPoseChannel *pchan = BKE_pose_channel_active_if_bonecoll_visible(ob);
|
||||
|
||||
if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) {
|
||||
break;
|
||||
}
|
||||
@@ -1428,8 +1429,11 @@ int getTransformOrientation_ex(const Scene *scene,
|
||||
bool ok = false;
|
||||
|
||||
if (activeOnly && (pchan = BKE_pose_channel_active_if_bonecoll_visible(ob))) {
|
||||
add_v3_v3(r_normal, pchan->pose_mat[2]);
|
||||
add_v3_v3(r_plane, pchan->pose_mat[1]);
|
||||
float pose_mat[3][3];
|
||||
BKE_pose_channel_transform_orientation(arm, pchan, pose_mat);
|
||||
|
||||
add_v3_v3(r_normal, pose_mat[2]);
|
||||
add_v3_v3(r_plane, pose_mat[1]);
|
||||
ok = true;
|
||||
}
|
||||
else {
|
||||
@@ -1439,8 +1443,11 @@ int getTransformOrientation_ex(const Scene *scene,
|
||||
/* Use channels to get stats. */
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
|
||||
if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) {
|
||||
add_v3_v3(r_normal, pchan->pose_mat[2]);
|
||||
add_v3_v3(r_plane, pchan->pose_mat[1]);
|
||||
float pose_mat[3][3];
|
||||
BKE_pose_channel_transform_orientation(arm, pchan, pose_mat);
|
||||
|
||||
add_v3_v3(r_normal, pose_mat[2]);
|
||||
add_v3_v3(r_plane, pose_mat[1]);
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
|
||||
@@ -303,7 +303,7 @@ typedef struct bPoseChannel {
|
||||
ListBase constraints;
|
||||
char name[/*MAXBONENAME*/ 64];
|
||||
|
||||
/** Dynamic, for detecting transform changes. */
|
||||
/** Dynamic, for detecting transform changes (ePchan_Flag). */
|
||||
short flag;
|
||||
/** Settings for IK bones. */
|
||||
short ikflag;
|
||||
@@ -454,7 +454,14 @@ typedef enum ePchan_Flag {
|
||||
|
||||
/* has BBone deforms */
|
||||
POSE_BBONE_SHAPE = (1 << 3),
|
||||
|
||||
/* When set and bPoseChan.custom_tx is not a nullptr, the gizmo will be drawn at the location and
|
||||
orientation of the custom_tx instead of this bone. */
|
||||
POSE_TRANSFORM_AT_CUSTOM_TX = (1 << 4),
|
||||
/* When set, transformations will modify the bone as if it was a child of the
|
||||
bPoseChan.custom_tx. The flag only has an effect when `POSE_TRANSFORM_AT_CUSTOM_TX` and
|
||||
`custom_tx` are set. This can be useful for rigs where the deformation is coming from
|
||||
blendshapes in addition to the armature. */
|
||||
POSE_TRANSFORM_AROUND_CUSTOM_TX = (1 << 5),
|
||||
/* IK/Pose solving */
|
||||
POSE_CHAIN = (1 << 9),
|
||||
POSE_DONE = (1 << 10),
|
||||
|
||||
@@ -1170,6 +1170,26 @@ static void rna_def_pose_channel(BlenderRNA *brna)
|
||||
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 100, RNA_TRANSLATION_PREC_DEFAULT);
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_transform_at_custom_shape", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", POSE_TRANSFORM_AT_CUSTOM_TX);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Affect Gizmo",
|
||||
"The location and orientation of the Custom Shape Transform bone will be used for transform "
|
||||
"gizmos and for other transform operators in the 3D Viewport. When disabled, the 3D "
|
||||
"Viewport will still use the actual bone transform for these, even when the custom bone "
|
||||
"shape transform is overridden.");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_transform_around_custom_shape", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", POSE_TRANSFORM_AROUND_CUSTOM_TX);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Use As Pivot",
|
||||
"Transform the bone as if it was a child of the Custom Shape Transform bone. This can be "
|
||||
"useful when combining shapekey and armature deformations.");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_shape_bone_size", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(
|
||||
prop, nullptr, "drawflag", PCHAN_DRAW_NO_CUSTOM_BONE_SIZE);
|
||||
|
||||
Reference in New Issue
Block a user