diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 9093b88205..3349c16e3b 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -1131,7 +1131,7 @@ void FSFloaterPoser::onUndoLastRotation() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.undoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.undoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle()); } enableOrDisableRedoButton(); @@ -1156,7 +1156,7 @@ void FSFloaterPoser::onUndoLastPosition() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.undoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.undoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedPositionSliders(); @@ -1180,7 +1180,7 @@ void FSFloaterPoser::onUndoLastScale() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.undoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.undoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedScaleSliders(); @@ -1220,7 +1220,7 @@ void FSFloaterPoser::onResetPosition() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.resetJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.resetJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedPositionSliders(); @@ -1247,7 +1247,7 @@ void FSFloaterPoser::onResetScale() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.resetJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.resetJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedScaleSliders(); @@ -1270,7 +1270,7 @@ void FSFloaterPoser::onRedoLastRotation() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.redoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.redoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle()); } enableOrDisableRedoButton(); @@ -1295,7 +1295,7 @@ void FSFloaterPoser::onRedoLastPosition() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.redoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.redoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedPositionSliders(); @@ -1319,7 +1319,7 @@ void FSFloaterPoser::onRedoLastScale() { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); if (currentlyPosing) - mPoserAnimator.redoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName())); + mPoserAnimator.redoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle()); } refreshAdvancedScaleSliders(); @@ -1443,39 +1443,25 @@ std::vector FSFloaterPoser::getUiSelectedPoserJo return joints; } -bool FSFloaterPoser::isAnyDeltaModeRotation(const E_BoneDeflectionStyles deflection) -{ - if (mToggleDeltaModeBtn->getValue().asBoolean()) - return true; - - switch (deflection) - { - case DELTAMODE: - case SYMPATHETIC_DELTA: - case MIRROR_DELTA: - return true; - - case NONE: - case SYMPATHETIC: - case MIRROR: - default: - return false; - } -} - -E_BoneDeflectionStyles FSFloaterPoser::getUiSelectedBoneDeflectionStyle(const std::string& jointName) const +E_RotationStyle FSFloaterPoser::getUiSelectedBoneRotationStyle(const std::string& jointName) const { if (jointName.empty()) - return NONE; + return ABSOLUTE_ROT; - bool isDelta = mToggleDeltaModeBtn->getValue().asBoolean(); bool hasRotationStylePreferenceParameter = hasString(XML_JOINT_DELTAROT_STRING_PREFIX + jointName); - if (hasRotationStylePreferenceParameter) - { - std::string paramValue = getString(XML_JOINT_DELTAROT_STRING_PREFIX + jointName); - if (strstr(paramValue.c_str(), "true")) - isDelta = true; - } + if (!hasRotationStylePreferenceParameter) + return ABSOLUTE_ROT; + + std::string paramValue = getString(XML_JOINT_DELTAROT_STRING_PREFIX + jointName); + if (strstr(paramValue.c_str(), "true")) + return DELTAIC_ROT; + + return ABSOLUTE_ROT; +} + +E_BoneDeflectionStyles FSFloaterPoser::getUiSelectedBoneDeflectionStyle() const +{ + bool isDelta = mToggleDeltaModeBtn->getValue().asBoolean(); if (mToggleMirrorRotationBtn->getValue().asBoolean()) return isDelta ? MIRROR_DELTA : MIRROR; @@ -1746,6 +1732,7 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z) return; LLVector3 vec3 = LLVector3(x, y, z); + E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); for (auto item : getUiSelectedPoserJoints()) { @@ -1753,7 +1740,6 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z) if (!currentlyPosingJoint) continue; - E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName()); mPoserAnimator.setJointPosition(avatar, item, vec3, defl); } } @@ -1767,8 +1753,9 @@ void FSFloaterPoser::setSelectedJointsRotation(LLVector3 absoluteRot, LLVector3 if (!mPoserAnimator.isPosingAvatar(avatar)) return; - auto selectedJoints = getUiSelectedPoserJoints(); - bool savingToExternal = getWhetherToResetBaseRotationOnEdit(); + auto selectedJoints = getUiSelectedPoserJoints(); + bool savingToExternal = getWhetherToResetBaseRotationOnEdit(); + E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); for (auto item : selectedJoints) { @@ -1786,9 +1773,9 @@ void FSFloaterPoser::setSelectedJointsRotation(LLVector3 absoluteRot, LLVector3 continue; } - E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName()); - mPoserAnimator.setJointRotation(avatar, item, isAnyDeltaModeRotation(defl) ? deltaRot : absoluteRot, defl, - getJointTranslation(item->jointName()), getJointNegation(item->jointName()), savingToExternal); + mPoserAnimator.setJointRotation(avatar, item, absoluteRot, deltaRot, defl, + getJointTranslation(item->jointName()), getJointNegation(item->jointName()), savingToExternal, + getUiSelectedBoneRotationStyle(item->jointName())); } if (savingToExternal) @@ -1804,7 +1791,8 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z) if (!mPoserAnimator.isPosingAvatar(avatar)) return; - LLVector3 vec3 = LLVector3(x, y, z); + LLVector3 vec3 = LLVector3(x, y, z); + E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); for (auto item : getUiSelectedPoserJoints()) { @@ -1812,7 +1800,6 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z) if (!currentlyPosingJoint) continue; - E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName()); mPoserAnimator.setJointScale(avatar, item, vec3, defl); } } diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index f079be6e64..07d4941497 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -147,16 +147,16 @@ class FSFloaterPoser : public LLFloater /// Gets the current bone-deflection style: encapsulates 'anything else you want to do' while you're manipulating a joint. /// Such as: fiddle the opposite joint too. /// - /// The well-known joint name of the joint to add the row for, eg: mChest. /// A E_BoneDeflectionStyles member. - E_BoneDeflectionStyles getUiSelectedBoneDeflectionStyle(const std::string& jointName) const; + E_BoneDeflectionStyles getUiSelectedBoneDeflectionStyle() const; /// - /// Gets whether the supplied joint name should be rotated using the delta method. + /// Gets the means by which the rotation should be applied to the supplied joint name. + /// Such as: fiddle the opposite joint too. /// - /// The deflection to consider. - /// true if the joint should be rotated by delta for any reason, otherwise false. - bool isAnyDeltaModeRotation(const E_BoneDeflectionStyles deflection); + /// The well-known joint name of the joint to add the row for, eg: mChest. + /// A E_RotationStyle member. + E_RotationStyle getUiSelectedBoneRotationStyle(const std::string& jointName) const; /// /// Gets the collection of UUIDs for nearby avatars. diff --git a/indra/newview/fsjointpose.cpp b/indra/newview/fsjointpose.cpp index 7c86f9c6cf..aa605e9f55 100644 --- a/indra/newview/fsjointpose.cpp +++ b/indra/newview/fsjointpose.cpp @@ -169,13 +169,30 @@ void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint) if (mIsCollisionVolume) return; - LLJoint* joint = mJointState->getJoint(); - if (!joint) + auto tempRot = FSJointRotation(mRotation); + mRotation = FSJointRotation(oppositeJoint->mRotation); + oppositeJoint->mRotation = tempRot; +} + +void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint) +{ + if (!fromJoint) return; - auto tempRot = FSJointRotation(mRotation); - mRotation = FSJointRotation(oppositeJoint->mRotation); - oppositeJoint->mRotation = tempRot; + mRotation = FSJointRotation(fromJoint->mRotation); +} + +void FSJointPose::mirrorRotationFrom(FSJointPose* fromJoint) +{ + if (!fromJoint) + return; + + cloneRotationFrom(fromJoint); + + mRotation.baseRotation = LLQuaternion(-mRotation.baseRotation.mQ[VX], mRotation.baseRotation.mQ[VY], -mRotation.baseRotation.mQ[VZ], + mRotation.baseRotation.mQ[VW]); + mRotation.deltaRotation = LLQuaternion(-mRotation.deltaRotation.mQ[VX], mRotation.deltaRotation.mQ[VY], -mRotation.deltaRotation.mQ[VZ], + mRotation.deltaRotation.mQ[VW]); } void FSJointPose::revertJointScale() diff --git a/indra/newview/fsjointpose.h b/indra/newview/fsjointpose.h index 5249451207..3983eef773 100644 --- a/indra/newview/fsjointpose.h +++ b/indra/newview/fsjointpose.h @@ -147,6 +147,16 @@ class FSJointPose /// void swapRotationWith(FSJointPose* oppositeJoint); + /// + /// Clones the rotation to this from the supplied joint. + /// + void cloneRotationFrom(FSJointPose* fromJoint); + + /// + /// Mirrors the rotation to this from the supplied joint. + /// + void mirrorRotationFrom(FSJointPose* fromJoint); + /// /// Resets the beginning properties of the joint this represents. /// diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 5309b9e92e..6845e8419c 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -496,8 +496,9 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi return translateRotationFromQuaternion(translation, negation, jointPose->getRotationDelta()); } -void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style, E_BoneAxisTranslation translation, S32 negation, - bool resetBaseRotationToZero) +void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, + const LLVector3& deltaRotation, E_BoneDeflectionStyles deflectionStyle, + E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle) { if (!isAvatarSafeToUse(avatar)) return; @@ -515,26 +516,35 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j if (resetBaseRotationToZero) jointPose->zeroBaseRotation(); - LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation); - switch (style) + LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation); + LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation); + switch (deflectionStyle) { case SYMPATHETIC: case MIRROR: - jointPose->setRotationDelta(rot_quat); + if (rotationStyle == DELTAIC_ROT) + jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta()); + else + jointPose->setRotationDelta(absRot); + break; case SYMPATHETIC_DELTA: case MIRROR_DELTA: - jointPose->setRotationDelta(rot_quat * jointPose->getRotationDelta()); + jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta()); break; case DELTAMODE: - jointPose->setRotationDelta(rot_quat * jointPose->getRotationDelta()); + jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta()); return; case NONE: default: - jointPose->setRotationDelta(rot_quat); + if (rotationStyle == DELTAIC_ROT) + jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta()); + else + jointPose->setRotationDelta(absRot); + return; } @@ -542,27 +552,23 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j if (!oppositeJointPose) return; - if (resetBaseRotationToZero) - oppositeJointPose->zeroBaseRotation(); - LLQuaternion inv_quat; - switch (style) + switch (deflectionStyle) { case SYMPATHETIC: - oppositeJointPose->setRotationDelta(rot_quat); + oppositeJointPose->cloneRotationFrom(jointPose); break; case SYMPATHETIC_DELTA: - oppositeJointPose->setRotationDelta(rot_quat * oppositeJointPose->getRotationDelta()); + oppositeJointPose->setRotationDelta(deltaRot * oppositeJointPose->getRotationDelta()); break; case MIRROR: - inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]); - oppositeJointPose->setRotationDelta(inv_quat); + oppositeJointPose->mirrorRotationFrom(jointPose); break; case MIRROR_DELTA: - inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]); + inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]); oppositeJointPose->setRotationDelta(inv_quat * oppositeJointPose->getRotationDelta()); break; diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 5c92d4b68a..109044b58b 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -54,10 +54,24 @@ typedef enum E_BoneDeflectionStyles MIRROR = 1, // change the other joint, like in a mirror, eg: one left one right SYMPATHETIC = 2, // change the other joint, but opposite to a mirrored way, eg: both go right or both go left DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current - MIRROR_DELTA = 4, // As MIRROR, but applied as negated delta to opposite - SYMPATHETIC_DELTA = 5, // As SYMPATHETIC, but applied as delta + MIRROR_DELTA = 4, // Applies a MIRROR delta, this limb and its opposite change by opposite amount + SYMPATHETIC_DELTA = 5, // Applies a SYMPATHETIC delta, this limb and the opposite change by the same amount } E_BoneDeflectionStyles; +/// +/// Joints may have rotations applied by applying an absolute value or a delta value. +/// When applying a rotation as absolutes, feedback via the UI can tend to Gimbal lock control of the quaternion. +/// For certain joints, particularly "down the centreline", absolute rotations provide the best feel. +/// For other joints, such as hips, knees, elbows and wrists, Gimbal lock readily occurs (sitting poses particularly), and +/// applying small angle changes directly to the quaternion (rather than going via the locked absolute) makes for +/// a more sensible user experience. +/// +typedef enum E_RotationStyle +{ + ABSOLUTE_ROT = 0, // The rotation should be applied as an absolute value because while it can Gimbal lock, it doesn't happen often. + DELTAIC_ROT = 1, // The rotation should be applied as a delta value because it is apt to Gimbal lock. +} E_RotationStyle; + /// /// When we're going from bone-rotation to the UI sliders, some of the axes need swapping so they make sense in UI-terms. /// eg: for one bone, the X-axis may mean up and down, but for another bone, the x-axis might be left-right. @@ -457,13 +471,15 @@ public: /// /// The avatar whose joint is to be set. /// The joint to set. - /// The rotation to set the joint to. + /// The absolute rotation to apply to the joint, if appropriate. + /// The delta of rotation to apply to the joint, if appropriate. /// Any ancilliary action to be taken with the change to be made. /// The axial translation form the supplied joint. /// The style of negation to apply to the set. /// Whether to set the base rotation to zero on setting the rotation. - void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style, - E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero); + /// Whether to apply the supplied rotation as a delta to the supplied joint. + void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, const LLVector3& deltaRotation, E_BoneDeflectionStyles style, + E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle); /// /// Gets the scale of a joint for the supplied avatar.