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
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user