forked from bartvdbraak/blender
2.5 - Rotation Locking for Bones
* Added Transform Locks panel. The layout for rotation I'm not satisfied with yet, though it is the best alternative so far. * Rotations can now be locked per-component for quats/axis-angle instead of going through eulers. This is currently enabled by the checkbox for the 'label' of the Lock Rotation column. - The naming of the property in RNA + the way this is presented in the UI can get some work done. - The setting for the 'w' component for quats/axis-angle is currently a separate flag in RNA, since I can't figure out how to lump this in under the 'lock_rotation' property instead (i.e. getting that to be either 3 or 4 components, depending on whether per-component locking is enabled). - Editing values directly should not be possible when these locks are set... * Fixed some tools which made use of this
This commit is contained in:
parent
3a9d99e3e8
commit
ad43aaf881
@ -72,6 +72,35 @@ class BONE_PT_transform(BoneButtonsPanel):
|
|||||||
col.itemL(text="Euler:")
|
col.itemL(text="Euler:")
|
||||||
col.row().itemR(pchan, "euler_rotation", text="")
|
col.row().itemR(pchan, "euler_rotation", text="")
|
||||||
|
|
||||||
|
class BONE_PT_transform_locks(BoneButtonsPanel):
|
||||||
|
__label__ = "Transform Locks"
|
||||||
|
|
||||||
|
def poll(self, context):
|
||||||
|
return context.bone
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
bone = context.bone
|
||||||
|
pchan = ob.pose.pose_channels[context.bone.name]
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
col = row.column()
|
||||||
|
col.itemR(pchan, "lock_location")
|
||||||
|
col.active = not (bone.parent and bone.connected)
|
||||||
|
|
||||||
|
col = row.column()
|
||||||
|
if pchan.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
|
||||||
|
col.itemR(pchan, "lock_rotations_4d", text="Lock Rotation")
|
||||||
|
if pchan.lock_rotations_4d:
|
||||||
|
col.itemR(pchan, "lock_rotation_w", text="W")
|
||||||
|
col.itemR(pchan, "lock_rotation", text="")
|
||||||
|
else:
|
||||||
|
col.itemR(pchan, "lock_rotation", text="Rotation")
|
||||||
|
|
||||||
|
row.column().itemR(pchan, "lock_scale")
|
||||||
|
|
||||||
class BONE_PT_bone(BoneButtonsPanel):
|
class BONE_PT_bone(BoneButtonsPanel):
|
||||||
__label__ = "Bone"
|
__label__ = "Bone"
|
||||||
|
|
||||||
@ -243,6 +272,7 @@ class BONE_PT_deform(BoneButtonsPanel):
|
|||||||
|
|
||||||
bpy.types.register(BONE_PT_context_bone)
|
bpy.types.register(BONE_PT_context_bone)
|
||||||
bpy.types.register(BONE_PT_transform)
|
bpy.types.register(BONE_PT_transform)
|
||||||
|
bpy.types.register(BONE_PT_transform_locks)
|
||||||
bpy.types.register(BONE_PT_bone)
|
bpy.types.register(BONE_PT_bone)
|
||||||
bpy.types.register(BONE_PT_deform)
|
bpy.types.register(BONE_PT_deform)
|
||||||
bpy.types.register(BONE_PT_inverse_kinematics)
|
bpy.types.register(BONE_PT_inverse_kinematics)
|
||||||
|
@ -4849,7 +4849,21 @@ static int pose_clear_rot_exec(bContext *C, wmOperator *op)
|
|||||||
|
|
||||||
/* only clear those channels that are not locked */
|
/* only clear those channels that are not locked */
|
||||||
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pchans) {
|
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pchans) {
|
||||||
if (pchan->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ)) {
|
if (pchan->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) {
|
||||||
|
/* check if convert to eulers for locking... */
|
||||||
|
if (pchan->protectflag & OB_LOCK_ROT4D) {
|
||||||
|
/* perform clamping on a component by component basis */
|
||||||
|
if ((pchan->protectflag & OB_LOCK_ROTW) == 0)
|
||||||
|
pchan->quat[0]= (pchan->rotmode == PCHAN_ROT_AXISANGLE) ? 0.0f : 1.0f;
|
||||||
|
if ((pchan->protectflag & OB_LOCK_ROTX) == 0)
|
||||||
|
pchan->quat[1]= 0.0f;
|
||||||
|
if ((pchan->protectflag & OB_LOCK_ROTY) == 0)
|
||||||
|
pchan->quat[2]= 0.0f;
|
||||||
|
if ((pchan->protectflag & OB_LOCK_ROTZ) == 0)
|
||||||
|
pchan->quat[3]= 0.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* perform clamping using euler form (3-components) */
|
||||||
float eul[3], oldeul[3], quat1[4];
|
float eul[3], oldeul[3], quat1[4];
|
||||||
|
|
||||||
if (pchan->rotmode == PCHAN_ROT_QUAT) {
|
if (pchan->rotmode == PCHAN_ROT_QUAT) {
|
||||||
@ -4857,7 +4871,7 @@ static int pose_clear_rot_exec(bContext *C, wmOperator *op)
|
|||||||
QuatToEul(pchan->quat, oldeul);
|
QuatToEul(pchan->quat, oldeul);
|
||||||
}
|
}
|
||||||
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
|
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
|
||||||
continue; // XXX
|
AxisAngleToEulO(&pchan->quat[1], pchan->quat[0], oldeul, EULER_ORDER_DEFAULT);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VECCOPY(oldeul, pchan->eul);
|
VECCOPY(oldeul, pchan->eul);
|
||||||
@ -4865,7 +4879,6 @@ static int pose_clear_rot_exec(bContext *C, wmOperator *op)
|
|||||||
|
|
||||||
eul[0]= eul[1]= eul[2]= 0.0f;
|
eul[0]= eul[1]= eul[2]= 0.0f;
|
||||||
|
|
||||||
// TODO: for 4 channel rotations, we need special flags for those too...
|
|
||||||
if (pchan->protectflag & OB_LOCK_ROTX)
|
if (pchan->protectflag & OB_LOCK_ROTX)
|
||||||
eul[0]= oldeul[0];
|
eul[0]= oldeul[0];
|
||||||
if (pchan->protectflag & OB_LOCK_ROTY)
|
if (pchan->protectflag & OB_LOCK_ROTY)
|
||||||
@ -4881,12 +4894,13 @@ static int pose_clear_rot_exec(bContext *C, wmOperator *op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
|
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
|
||||||
// TODO...
|
AxisAngleToEulO(&pchan->quat[1], pchan->quat[0], oldeul, EULER_ORDER_DEFAULT);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VECCOPY(pchan->eul, eul);
|
VECCOPY(pchan->eul, eul);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (pchan->rotmode == PCHAN_ROT_QUAT) {
|
if (pchan->rotmode == PCHAN_ROT_QUAT) {
|
||||||
pchan->quat[1]=pchan->quat[2]=pchan->quat[3]= 0.0f;
|
pchan->quat[1]=pchan->quat[2]=pchan->quat[3]= 0.0f;
|
||||||
|
@ -998,7 +998,6 @@ static int pose_paste_exec (bContext *C, wmOperator *op)
|
|||||||
pchan->flag= chan->flag;
|
pchan->flag= chan->flag;
|
||||||
|
|
||||||
/* check if rotation modes are compatible (i.e. do they need any conversions) */
|
/* check if rotation modes are compatible (i.e. do they need any conversions) */
|
||||||
// FIXME: add axis-angle here too...
|
|
||||||
if (pchan->rotmode == chan->rotmode) {
|
if (pchan->rotmode == chan->rotmode) {
|
||||||
/* copy the type of rotation in use */
|
/* copy the type of rotation in use */
|
||||||
if (pchan->rotmode > 0) {
|
if (pchan->rotmode > 0) {
|
||||||
|
@ -1610,13 +1610,71 @@ static void protectedRotateBits(short protectflag, float *eul, float *oldeul)
|
|||||||
eul[2]= oldeul[2];
|
eul[2]= oldeul[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* this function only does the delta rotation */
|
||||||
|
/* axis-angle is usually internally stored as quats... */
|
||||||
|
static void protectedAxisAngleBits(short protectflag, float *quat, float *oldquat)
|
||||||
|
{
|
||||||
|
/* check that protection flags are set */
|
||||||
|
if ((protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (protectflag & OB_LOCK_ROT4D) {
|
||||||
|
/* axis-angle getting limited as 4D entities that they are... */
|
||||||
|
if (protectflag & OB_LOCK_ROTW)
|
||||||
|
quat[0]= oldquat[0];
|
||||||
|
if (protectflag & OB_LOCK_ROTX)
|
||||||
|
quat[1]= oldquat[1];
|
||||||
|
if (protectflag & OB_LOCK_ROTY)
|
||||||
|
quat[2]= oldquat[2];
|
||||||
|
if (protectflag & OB_LOCK_ROTZ)
|
||||||
|
quat[3]= oldquat[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* axis-angle get limited with euler... */
|
||||||
|
float eul[3], oldeul[3], quat1[4];
|
||||||
|
|
||||||
|
QUATCOPY(quat1, quat);
|
||||||
|
AxisAngleToEulO(quat+1, quat[0], eul, EULER_ORDER_DEFAULT);
|
||||||
|
AxisAngleToEulO(oldquat+1, oldquat[0], oldeul, EULER_ORDER_DEFAULT);
|
||||||
|
|
||||||
|
if (protectflag & OB_LOCK_ROTX)
|
||||||
|
eul[0]= oldeul[0];
|
||||||
|
if (protectflag & OB_LOCK_ROTY)
|
||||||
|
eul[1]= oldeul[1];
|
||||||
|
if (protectflag & OB_LOCK_ROTZ)
|
||||||
|
eul[2]= oldeul[2];
|
||||||
|
|
||||||
|
EulOToAxisAngle(eul, EULER_ORDER_DEFAULT, quat+1, quat);
|
||||||
|
|
||||||
|
/* when converting to axis-angle, we need a special exception for the case when there is no axis */
|
||||||
|
if (IS_EQ(quat[1], quat[2]) && IS_EQ(quat[2], quat[3])) {
|
||||||
|
/* for now, rotate around y-axis then (so that it simply becomes the roll) */
|
||||||
|
quat[2]= 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this function only does the delta rotation */
|
||||||
static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat)
|
static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat)
|
||||||
{
|
{
|
||||||
/* quaternions get limited with euler... */
|
/* check that protection flags are set */
|
||||||
/* this function only does the delta rotation */
|
if ((protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// FIXME: need special checks for quality here...
|
if (protectflag & OB_LOCK_ROT4D) {
|
||||||
if(protectflag) {
|
/* quaternions getting limited as 4D entities that they are... */
|
||||||
|
if (protectflag & OB_LOCK_ROTW)
|
||||||
|
quat[0]= oldquat[0];
|
||||||
|
if (protectflag & OB_LOCK_ROTX)
|
||||||
|
quat[1]= oldquat[1];
|
||||||
|
if (protectflag & OB_LOCK_ROTY)
|
||||||
|
quat[2]= oldquat[2];
|
||||||
|
if (protectflag & OB_LOCK_ROTZ)
|
||||||
|
quat[3]= oldquat[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* quaternions get limited with euler... (compatability mode) */
|
||||||
float eul[3], oldeul[3], quat1[4];
|
float eul[3], oldeul[3], quat1[4];
|
||||||
|
|
||||||
QUATCOPY(quat1, quat);
|
QUATCOPY(quat1, quat);
|
||||||
@ -1631,6 +1689,7 @@ static void protectedQuaternionBits(short protectflag, float *quat, float *oldqu
|
|||||||
eul[2]= oldeul[2];
|
eul[2]= oldeul[2];
|
||||||
|
|
||||||
EulToQuat(eul, quat);
|
EulToQuat(eul, quat);
|
||||||
|
|
||||||
/* quaternions flip w sign to accumulate rotations correctly */
|
/* quaternions flip w sign to accumulate rotations correctly */
|
||||||
if ( (quat1[0]<0.0f && quat[0]>0.0f) || (quat1[0]>0.0f && quat[0]<0.0f) ) {
|
if ( (quat1[0]<0.0f && quat[0]>0.0f) || (quat1[0]>0.0f && quat[0]<0.0f) ) {
|
||||||
QuatMulf(quat, -1.0f);
|
QuatMulf(quat, -1.0f);
|
||||||
@ -1733,7 +1792,7 @@ static void constraintRotLim(TransInfo *t, TransData *td)
|
|||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (td->tdi) {
|
else if (td->tdi) { // XXX depreceated
|
||||||
/* ipo-keys eulers */
|
/* ipo-keys eulers */
|
||||||
TransDataIpokey *tdi= td->tdi;
|
TransDataIpokey *tdi= td->tdi;
|
||||||
float eul[3];
|
float eul[3];
|
||||||
@ -1744,6 +1803,13 @@ static void constraintRotLim(TransInfo *t, TransData *td)
|
|||||||
|
|
||||||
EulOToMat4(eul, td->rotOrder, cob.matrix);
|
EulOToMat4(eul, td->rotOrder, cob.matrix);
|
||||||
}
|
}
|
||||||
|
else if (td->rotOrder == PCHAN_ROT_AXISANGLE) {
|
||||||
|
/* axis angle */
|
||||||
|
if (td->ext)
|
||||||
|
AxisAngleToMat4(&td->ext->quat[1], td->ext->quat[0], cob.matrix);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* eulers */
|
/* eulers */
|
||||||
if (td->ext)
|
if (td->ext)
|
||||||
@ -1806,6 +1872,10 @@ static void constraintRotLim(TransInfo *t, TransData *td)
|
|||||||
tdi->roty[0]= eul[1];
|
tdi->roty[0]= eul[1];
|
||||||
tdi->rotz[0]= eul[2];
|
tdi->rotz[0]= eul[2];
|
||||||
}
|
}
|
||||||
|
else if (td->rotOrder == PCHAN_ROT_AXISANGLE) {
|
||||||
|
/* axis angle */
|
||||||
|
Mat4ToAxisAngle(cob.matrix, &td->ext->quat[1], &td->ext->quat[0]);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* eulers */
|
/* eulers */
|
||||||
Mat4ToEulO(cob.matrix, td->ext->rot, td->rotOrder);
|
Mat4ToEulO(cob.matrix, td->ext->rot, td->rotOrder);
|
||||||
@ -2688,12 +2758,11 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short
|
|||||||
QuatMul(td->ext->quat, quat, iquat);
|
QuatMul(td->ext->quat, quat, iquat);
|
||||||
|
|
||||||
/* make temp copy (since stored in same place) */
|
/* make temp copy (since stored in same place) */
|
||||||
QuatCopy(quat, td->ext->quat); // this is just a 4d vector copying function
|
QUATCOPY(quat, td->ext->quat); // this is just a 4d vector copying macro
|
||||||
QuatToAxisAngle(quat, &td->ext->quat[1], &td->ext->quat[0]);
|
QuatToAxisAngle(quat, &td->ext->quat[1], &td->ext->quat[0]);
|
||||||
|
|
||||||
/* this function works on end result */
|
/* this function works on end result */
|
||||||
// TODO: we really need a specialised version of this for axis-angle that doesn't try to do quats...
|
protectedAxisAngleBits(td->protectflag, td->ext->quat, td->ext->iquat);
|
||||||
protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float eulmat[3][3];
|
float eulmat[3][3];
|
||||||
|
@ -515,6 +515,8 @@ extern Object workob;
|
|||||||
#define OB_LOCK_SCALEY 128
|
#define OB_LOCK_SCALEY 128
|
||||||
#define OB_LOCK_SCALEZ 256
|
#define OB_LOCK_SCALEZ 256
|
||||||
#define OB_LOCK_SCALE 448
|
#define OB_LOCK_SCALE 448
|
||||||
|
#define OB_LOCK_ROTW 512
|
||||||
|
#define OB_LOCK_ROT4D 1024
|
||||||
|
|
||||||
/* ob->mode */
|
/* ob->mode */
|
||||||
typedef enum ObjectMode {
|
typedef enum ObjectMode {
|
||||||
|
@ -698,14 +698,19 @@ static void rna_def_pose_channel(BlenderRNA *brna)
|
|||||||
prop= RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_XYZ);
|
prop= RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_XYZ);
|
||||||
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTX);
|
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTX);
|
||||||
RNA_def_property_array(prop, 3);
|
RNA_def_property_array(prop, 3);
|
||||||
RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation (with three components) in the interface.");
|
RNA_def_property_ui_text(prop, "Lock Rotation", "Lock editing of rotation in the interface.");
|
||||||
RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
|
RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
|
||||||
|
|
||||||
//prop= RNA_def_property(srna, "lock_rotation_4d", PROP_BOOLEAN, PROP_XYZ);
|
// XXX this is sub-optimal - it really should be included above, but due to technical reasons we can't do this!
|
||||||
//RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTW);
|
prop= RNA_def_property(srna, "lock_rotation_w", PROP_BOOLEAN, PROP_NONE);
|
||||||
//RNA_def_property_array(prop, 4);
|
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTW);
|
||||||
//RNA_def_property_ui_text(prop, "Lock Rotation (4D)", "Lock editing of rotations (with four components) in the interface.");
|
RNA_def_property_ui_text(prop, "Lock Rotation (4D Angle)", "Lock editing of 'angle' component of four-component rotations in the interface.");
|
||||||
//RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
|
RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
|
||||||
|
// XXX this needs a better name
|
||||||
|
prop= RNA_def_property(srna, "lock_rotations_4d", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROT4D);
|
||||||
|
RNA_def_property_ui_text(prop, "Lock Rotations (4D)", "Lock editing of four component rotations by components (instead of as Eulers).");
|
||||||
|
RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
|
||||||
|
|
||||||
prop= RNA_def_property(srna, "lock_scale", PROP_BOOLEAN, PROP_XYZ);
|
prop= RNA_def_property(srna, "lock_scale", PROP_BOOLEAN, PROP_XYZ);
|
||||||
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_SCALEX);
|
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_SCALEX);
|
||||||
|
Loading…
Reference in New Issue
Block a user