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:
Joshua Leung 2009-09-12 12:30:23 +00:00
parent 3a9d99e3e8
commit ad43aaf881
6 changed files with 186 additions and 67 deletions

@ -71,6 +71,35 @@ class BONE_PT_transform(BoneButtonsPanel):
col = layout.column(align=True)
col.itemL(text="Euler:")
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):
__label__ = "Bone"
@ -243,6 +272,7 @@ class BONE_PT_deform(BoneButtonsPanel):
bpy.types.register(BONE_PT_context_bone)
bpy.types.register(BONE_PT_transform)
bpy.types.register(BONE_PT_transform_locks)
bpy.types.register(BONE_PT_bone)
bpy.types.register(BONE_PT_deform)
bpy.types.register(BONE_PT_inverse_kinematics)

@ -4849,42 +4849,56 @@ static int pose_clear_rot_exec(bContext *C, wmOperator *op)
/* only clear those channels that are not locked */
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pchans) {
if (pchan->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ)) {
float eul[3], oldeul[3], quat1[4];
if (pchan->rotmode == PCHAN_ROT_QUAT) {
QUATCOPY(quat1, pchan->quat);
QuatToEul(pchan->quat, oldeul);
}
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
continue; // XXX
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 {
VECCOPY(oldeul, pchan->eul);
}
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)
eul[0]= oldeul[0];
if (pchan->protectflag & OB_LOCK_ROTY)
eul[1]= oldeul[1];
if (pchan->protectflag & OB_LOCK_ROTZ)
eul[2]= oldeul[2];
if (pchan->rotmode == PCHAN_ROT_QUAT) {
EulToQuat(eul, pchan->quat);
/* quaternions flip w sign to accumulate rotations correctly */
if ((quat1[0]<0.0f && pchan->quat[0]>0.0f) || (quat1[0]>0.0f && pchan->quat[0]<0.0f)) {
QuatMulf(pchan->quat, -1.0f);
/* perform clamping using euler form (3-components) */
float eul[3], oldeul[3], quat1[4];
if (pchan->rotmode == PCHAN_ROT_QUAT) {
QUATCOPY(quat1, pchan->quat);
QuatToEul(pchan->quat, oldeul);
}
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
AxisAngleToEulO(&pchan->quat[1], pchan->quat[0], oldeul, EULER_ORDER_DEFAULT);
}
else {
VECCOPY(oldeul, pchan->eul);
}
eul[0]= eul[1]= eul[2]= 0.0f;
if (pchan->protectflag & OB_LOCK_ROTX)
eul[0]= oldeul[0];
if (pchan->protectflag & OB_LOCK_ROTY)
eul[1]= oldeul[1];
if (pchan->protectflag & OB_LOCK_ROTZ)
eul[2]= oldeul[2];
if (pchan->rotmode == PCHAN_ROT_QUAT) {
EulToQuat(eul, pchan->quat);
/* quaternions flip w sign to accumulate rotations correctly */
if ((quat1[0]<0.0f && pchan->quat[0]>0.0f) || (quat1[0]>0.0f && pchan->quat[0]<0.0f)) {
QuatMulf(pchan->quat, -1.0f);
}
}
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
AxisAngleToEulO(&pchan->quat[1], pchan->quat[0], oldeul, EULER_ORDER_DEFAULT);
}
else {
VECCOPY(pchan->eul, eul);
}
}
else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
// TODO...
}
else {
VECCOPY(pchan->eul, eul);
}
}
else {

@ -998,7 +998,6 @@ static int pose_paste_exec (bContext *C, wmOperator *op)
pchan->flag= chan->flag;
/* check if rotation modes are compatible (i.e. do they need any conversions) */
// FIXME: add axis-angle here too...
if (pchan->rotmode == chan->rotmode) {
/* copy the type of rotation in use */
if (pchan->rotmode > 0) {

@ -1610,29 +1610,88 @@ static void protectedRotateBits(short protectflag, float *eul, float *oldeul)
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)
{
/* quaternions get limited with euler... */
/* this function only does the delta rotation */
/* check that protection flags are set */
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) {
if (protectflag & OB_LOCK_ROT4D) {
/* 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];
QUATCOPY(quat1, quat);
QuatToEul(quat, eul);
QuatToEul(oldquat, oldeul);
if(protectflag & OB_LOCK_ROTX)
if (protectflag & OB_LOCK_ROTX)
eul[0]= oldeul[0];
if(protectflag & OB_LOCK_ROTY)
if (protectflag & OB_LOCK_ROTY)
eul[1]= oldeul[1];
if(protectflag & OB_LOCK_ROTZ)
if (protectflag & OB_LOCK_ROTZ)
eul[2]= oldeul[2];
EulToQuat(eul, quat);
/* 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);
}
}
@ -1733,17 +1792,24 @@ static void constraintRotLim(TransInfo *t, TransData *td)
else
return;
}
else if (td->tdi) {
else if (td->tdi) { // XXX depreceated
/* ipo-keys eulers */
TransDataIpokey *tdi= td->tdi;
float eul[3];
eul[0]= tdi->rotx[0];
eul[1]= tdi->roty[0];
eul[2]= tdi->rotz[0];
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 {
/* eulers */
if (td->ext)
@ -1751,22 +1817,22 @@ static void constraintRotLim(TransInfo *t, TransData *td)
else
return;
}
/* Evaluate valid constraints */
for (con= td->con; con; con= con->next) {
/* only consider constraint if enabled */
if (con->flag & CONSTRAINT_DISABLE) continue;
if (con->enforce == 0.0f) continue;
/* we're only interested in Limit-Rotation constraints */
if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
bRotLimitConstraint *data= con->data;
float tmat[4][4];
/* only use it if it's tagged for this purpose */
if ((data->flag2 & LIMIT_TRANSFORM)==0)
continue;
/* do space conversions */
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
/* just multiply by td->mtx (this should be ok) */
@ -1777,10 +1843,10 @@ static void constraintRotLim(TransInfo *t, TransData *td)
/* skip... incompatable spacetype */
continue;
}
/* do constraint */
cti->evaluate_constraint(con, &cob, NULL);
/* convert spaces again */
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
/* just multiply by td->mtx (this should be ok) */
@ -1789,7 +1855,7 @@ static void constraintRotLim(TransInfo *t, TransData *td)
}
}
}
/* copy results from cob->matrix */
if (td->flag & TD_USEQUAT) {
/* quats */
@ -1799,13 +1865,17 @@ static void constraintRotLim(TransInfo *t, TransData *td)
/* ipo-keys eulers */
TransDataIpokey *tdi= td->tdi;
float eul[3];
Mat4ToEulO(cob.matrix, eul, td->rotOrder);
tdi->rotx[0]= eul[0];
tdi->roty[0]= eul[1];
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 {
/* eulers */
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);
/* 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]);
/* 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...
protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat);
protectedAxisAngleBits(td->protectflag, td->ext->quat, td->ext->iquat);
}
else {
float eulmat[3][3];
@ -2714,7 +2783,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short
protectedRotateBits(td->protectflag, eul, td->ext->irot);
VECCOPY(td->ext->rot, eul);
}
constraintRotLim(t, td);
}
}

@ -515,6 +515,8 @@ extern Object workob;
#define OB_LOCK_SCALEY 128
#define OB_LOCK_SCALEZ 256
#define OB_LOCK_SCALE 448
#define OB_LOCK_ROTW 512
#define OB_LOCK_ROT4D 1024
/* ob->mode */
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);
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTX);
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");
//prop= RNA_def_property(srna, "lock_rotation_4d", PROP_BOOLEAN, PROP_XYZ);
//RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTW);
//RNA_def_property_array(prop, 4);
//RNA_def_property_ui_text(prop, "Lock Rotation (4D)", "Lock editing of rotations (with four components) in the interface.");
//RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update");
// XXX this is sub-optimal - it really should be included above, but due to technical reasons we can't do this!
prop= RNA_def_property(srna, "lock_rotation_w", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_ROTW);
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");
// 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);
RNA_def_property_boolean_sdna(prop, NULL, "protectflag", OB_LOCK_SCALEX);