From 7e761d5b0e06c2b8ff9a913ccc26abc1e0333bea Mon Sep 17 00:00:00 2001 From: Angeldark Raymaker Date: Sun, 15 Sep 2024 21:26:47 +0100 Subject: [PATCH] FIRE-30873: Add flip-joint button and refactor flipping --- indra/newview/fsfloaterposer.cpp | 65 +++++++++++++---- indra/newview/fsfloaterposer.h | 1 + indra/newview/fsposeranimator.cpp | 40 ++++++++-- indra/newview/fsposeranimator.h | 73 +++++++++++-------- .../skins/default/xui/en/floater_poser.xml | 23 +++++- 5 files changed, 145 insertions(+), 57 deletions(-) diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 3dbc862e1b..336b195158 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -85,6 +85,7 @@ static const std::string POSER_AVATAR_STARTSTOP_POSING_BUTTON_NAME = "start_stop static const std::string POSER_AVATAR_ADVANCED_TOGGLEBUTTON_NAME = "toggleAdvancedPanel"; static const std::string POSER_AVATAR_PANEL_ADVANCED_NAME = "poses_AdvancedControls"; static const std::string POSER_AVATAR_PANEL_BUTTON_FLIPPOSE_NAME = "FlipPose_avatar"; +static const std::string POSER_AVATAR_PANEL_BUTTON_FLIPJOINT_NAME = "FlipJoint_avatar"; static const std::string POSER_AVATAR_PANEL_BUTTON_RECAPTURE_NAME = "button_RecaptureParts"; static const std::string POSER_AVATAR_PANEL_BUTTON_TOGGLEPOSING_NAME = "toggle_PosingSelectedBones"; @@ -128,6 +129,7 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) mCommitCallbackRegistrar.add("Poser.BrowseCache", boost::bind(&FSFloaterPoser::onClickBrowsePoseCache, this)); mCommitCallbackRegistrar.add("Poser.FlipPose", boost::bind(&FSFloaterPoser::onClickFlipPose, this)); + mCommitCallbackRegistrar.add("Poser.FlipJoint", boost::bind(&FSFloaterPoser::onClickFlipSelectedJoints, this)); mCommitCallbackRegistrar.add("Poser.RecaptureSelectedBones", boost::bind(&FSFloaterPoser::onClickRecaptureSelectedBones, this)); mCommitCallbackRegistrar.add("Poser.TogglePosingSelectedBones", boost::bind(&FSFloaterPoser::onClickToggleSelectedBoneEnabled, this)); mCommitCallbackRegistrar.add("Pose.PoseResetMenu", boost::bind(&FSFloaterPoser::onPoseResetMenuAction, this, _2)); @@ -394,6 +396,49 @@ void FSFloaterPoser::onClickToggleSelectedBoneEnabled() refreshTextEmbiggeningOnAllScrollLists(); } +void FSFloaterPoser::onClickFlipSelectedJoints() +{ + auto selectedJoints = getUiSelectedPoserJoints(); + if (selectedJoints.size() < 1) + return; + + LLVOAvatar *avatar = getUiSelectedAvatar(); + if (!avatar) + return; + + if (!_poserAnimator.isPosingAvatar(avatar)) + return; + + for (auto item : selectedJoints) + { + // need to be posing the joint to flippit + bool currentlyPosingJoint = _poserAnimator.isPosingAvatarJoint(avatar, *item); + if (!currentlyPosingJoint) + continue; + + // need to be posing opposite joint to flipthat + auto oppositeJoint = _poserAnimator.getPoserJointByName(item->mirrorJointName()); + if (oppositeJoint) + { + bool currentlyPosingOppositeJoint = _poserAnimator.isPosingAvatarJoint(avatar, *oppositeJoint); + if (!currentlyPosingOppositeJoint) + continue; + } + + // if you selected a joint and its opposite, we would flip both of them to yeild no net result (other than a confused user). + if (std::find(selectedJoints.begin(), selectedJoints.end(), oppositeJoint) != selectedJoints.end()) + { + if (!item->dontFlipOnMirror()) + continue; + } + + _poserAnimator.reflectJoint(avatar, item); // flippit good! + } + + refreshRotationSliders(); + refreshTrackpadCursor(); +} + void FSFloaterPoser::onClickFlipPose() { LLVOAvatar *avatar = getUiSelectedAvatar(); @@ -403,21 +448,7 @@ void FSFloaterPoser::onClickFlipPose() if (!_poserAnimator.isPosingAvatar(avatar)) return; - LLVector3 unNeededPosition; - std::vector::const_iterator poserJoint_iter; - for (poserJoint_iter = _poserAnimator.PoserJoints.begin(); poserJoint_iter != _poserAnimator.PoserJoints.end(); ++poserJoint_iter) - { - if (strstr(poserJoint_iter->jointName().c_str(), "Left")) // don't do left, just do one side of body; TODO refactor - continue; - - bool currentlyPosing = _poserAnimator.isPosingAvatarJoint(avatar, *poserJoint_iter); // TODO check opposite is off? - if (!currentlyPosing) - continue; - - _poserAnimator.setJointRotation(avatar, &*poserJoint_iter, unNeededPosition, REFLECT_JOINT, - getJointTranslation(poserJoint_iter->jointName()), - getJointNegation(poserJoint_iter->jointName())); - } + _poserAnimator.flipEntirePose(avatar); refreshRotationSliders(); refreshTrackpadCursor(); @@ -687,6 +718,10 @@ void FSFloaterPoser::poseControlsEnable(bool enable) if (someButton) someButton->setEnabled(enable); + someButton = getChild(POSER_AVATAR_PANEL_BUTTON_FLIPJOINT_NAME); + if (someButton) + someButton->setEnabled(enable); + someButton = getChild(POSER_AVATAR_PANEL_BUTTON_RECAPTURE_NAME); if (someButton) someButton->setEnabled(enable); diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 4aa101c548..8142979631 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -192,6 +192,7 @@ class FSFloaterPoser : public LLFloater void onClickToggleSelectedBoneEnabled(); void onClickRecaptureSelectedBones(); void onClickFlipPose(); + void onClickFlipSelectedJoints(); void onPoseResetMenuAction(const LLSD ¶m); // UI Refreshments diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 6274938949..d43cc631ea 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -153,12 +153,6 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar *avatar, const FSPoserJoint *j if (!joint) return; - if (style == REFLECT_JOINT) - { - reflectJoint(avatar, joint, translation, negation); - return; - } - LLJoint *avJoint = avatar->getJoint(JointKey::construct(joint->jointName())); if (!avJoint) return; @@ -190,8 +184,14 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar *avatar, const FSPoserJoint *j } } -void FSPoserAnimator::reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint, E_BoneAxisTranslation translation, S32 negation) +void FSPoserAnimator::reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint) { + if (!avatar || avatar->isDead()) + return; + + if (!joint) + return; + LLJoint *avJoint = avatar->getJoint(JointKey::construct(joint->jointName())); if (!avJoint) return; @@ -214,6 +214,32 @@ void FSPoserAnimator::reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint oppositeJoint->setTargetRotation(first_inv); } +void FSPoserAnimator::flipEntirePose(LLVOAvatar *avatar) +{ + if (!avatar || avatar->isDead()) + return; + + for (size_t index = 0; index != PoserJoints.size(); ++index) + { + if (PoserJoints[index].dontFlipOnMirror()) // we only flip one side. + continue; + + bool currentlyPosing = isPosingAvatarJoint(avatar, PoserJoints[index]); + if (!currentlyPosing) + continue; + + auto oppositeJoint = getPoserJointByName(PoserJoints[index].mirrorJointName()); + if (oppositeJoint) + { + bool currentlyPosingOppositeJoint = isPosingAvatarJoint(avatar, *oppositeJoint); + if (!currentlyPosingOppositeJoint) + continue; + } + + reflectJoint(avatar, &PoserJoints[index]); + } +} + // from the UI to the bone, the inverse translation, the un-swap, the backwards LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation) diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 438e69f771..7ff1760a1e 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -54,7 +54,6 @@ typedef enum E_BoneDeflectionStyles NONE = 0, // do nothing additional 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 - REFLECT_JOINT = 3, // change each joint, mirroring to its opposite and mirror each joint without an opposite } E_BoneDeflectionStyles; /// @@ -91,12 +90,14 @@ public: /// /// A class encapsulating 'metadata' for a joint, such as its catagory and its opposite joint name. + /// You'll note it's privates and methods: this is just emulating { get; private set; } from C# /// class FSPoserJoint { std::string _jointName; // expected to be a match to LLJoint.getName() for a joint implementation. std::string _mirrorJointName; E_BoneTypes _boneList; + bool _dontFlipOnMirror = false; public: /// /// Gets the name of the joint. @@ -113,6 +114,11 @@ public: /// E_BoneTypes boneType() const { return _boneList; } + /// + /// Gets whether when mirroring the entire body, should this joint flip its counterpart. + /// + bool dontFlipOnMirror() const { return _dontFlipOnMirror; } + /// /// Creates a new instance of a PoserJoint. /// @@ -123,11 +129,12 @@ public: /// /// The opposite joint name, if any. Also expected to be a well-known name. /// The - FSPoserJoint(std::string a, std::string b, E_BoneTypes c) + FSPoserJoint(std::string a, std::string b, E_BoneTypes c, bool d = false) { - _jointName = a; - _mirrorJointName = b; - _boneList = c; + _jointName = a; + _mirrorJointName = b; + _boneList = c; + _dontFlipOnMirror = d; } }; @@ -143,22 +150,22 @@ public: // head, torso, legs {"mPelvis", "", WHOLEAVATAR}, {"mTorso", "", BODY}, {"mChest", "", BODY}, {"mNeck", "", BODY}, {"mHead", "", BODY}, {"mCollarLeft", "mCollarRight", BODY}, {"mShoulderLeft", "mShoulderRight", BODY}, {"mElbowLeft", "mElbowRight", BODY}, {"mWristLeft", "mWristRight", BODY}, - {"mCollarRight", "mCollarLeft", BODY}, {"mShoulderRight", "mShoulderLeft", BODY}, {"mElbowRight", "mElbowLeft", BODY}, {"mWristRight", "mWristLeft", BODY}, + {"mCollarRight", "mCollarLeft", BODY, true}, {"mShoulderRight", "mShoulderLeft", BODY, true}, {"mElbowRight", "mElbowLeft", BODY, true}, {"mWristRight", "mWristLeft", BODY, true}, {"mHipLeft", "mHipRight", BODY}, {"mKneeLeft", "mKneeRight", BODY}, {"mAnkleLeft", "mAnkleRight", BODY}, - {"mHipRight", "mHipLeft", BODY}, {"mKneeRight", "mKneeLeft", BODY}, {"mAnkleRight", "mAnkleLeft", BODY}, + {"mHipRight", "mHipLeft", BODY, true}, {"mKneeRight", "mKneeLeft", BODY, true}, {"mAnkleRight", "mAnkleLeft", BODY, true}, // face - {"mFaceForeheadLeft", "mFaceForeheadRight", FACE}, {"mFaceForeheadCenter", "", FACE}, {"mFaceForeheadRight", "mFaceForeheadLeft", FACE}, + {"mFaceForeheadLeft", "mFaceForeheadRight", FACE}, {"mFaceForeheadCenter", "", FACE}, {"mFaceForeheadRight", "mFaceForeheadLeft", FACE, true}, {"mFaceEyebrowOuterLeft", "mFaceEyebrowOuterRight", FACE}, {"mFaceEyebrowCenterLeft", "mFaceEyebrowCenterRight", FACE}, {"mFaceEyebrowInnerLeft", "mFaceEyebrowInnerRight", FACE}, - {"mFaceEyebrowOuterRight", "mFaceEyebrowOuterLeft", FACE}, {"mFaceEyebrowCenterRight", "mFaceEyebrowCenterLeft", FACE}, {"mFaceEyebrowInnerRight", "mFaceEyebrowInnerLeft", FACE}, + {"mFaceEyebrowOuterRight", "mFaceEyebrowOuterLeft", FACE, true}, {"mFaceEyebrowCenterRight", "mFaceEyebrowCenterLeft", FACE, true}, {"mFaceEyebrowInnerRight", "mFaceEyebrowInnerLeft", FACE, true}, - {"mEyeLeft", "mEyeRight", FACE}, {"mEyeRight", "mEyeLeft", FACE}, - {"mFaceEyeLidUpperLeft", "mFaceEyeLidUpperRight", FACE}, {"mFaceEyeLidLowerLeft", "mFaceEyeLidLowerRight", FACE}, {"mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE}, {"mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE}, + {"mEyeLeft", "mEyeRight", FACE}, {"mEyeRight", "mEyeLeft", FACE, true}, + {"mFaceEyeLidUpperLeft", "mFaceEyeLidUpperRight", FACE}, {"mFaceEyeLidLowerLeft", "mFaceEyeLidLowerRight", FACE}, {"mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE, true}, {"mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE, true}, - {"mFaceCheekUpperLeft", "mFaceCheekUpperRight", FACE}, {"mFaceCheekLowerLeft", "mFaceCheekLowerRight", FACE}, {"mFaceCheekUpperRight", "mFaceCheekUpperLeft", FACE}, {"mFaceCheekLowerRight", "mFaceCheekLowerLeft", FACE}, - {"mFaceLipUpperLeft", "mFaceLipUpperRight", FACE}, {"mFaceLipUpperCenter", "", FACE}, {"mFaceLipUpperRight", "mFaceLipUpperLeft", FACE}, - {"mFaceLipCornerLeft", "mFaceLipCornerRight", FACE}, {"mFaceLipCornerRight", "mFaceLipCornerLeft", FACE}, - {"mFaceLipLowerLeft", "mFaceLipLowerRight", FACE}, {"mFaceLipLowerCenter", "", FACE}, {"mFaceLipLowerRight", "mFaceLipLowerLeft", FACE}, + {"mFaceCheekUpperLeft", "mFaceCheekUpperRight", FACE}, {"mFaceCheekLowerLeft", "mFaceCheekLowerRight", FACE}, {"mFaceCheekUpperRight", "mFaceCheekUpperLeft", FACE, true}, {"mFaceCheekLowerRight", "mFaceCheekLowerLeft", FACE, true}, + {"mFaceLipUpperLeft", "mFaceLipUpperRight", FACE}, {"mFaceLipUpperCenter", "", FACE}, {"mFaceLipUpperRight", "mFaceLipUpperLeft", FACE, true}, + {"mFaceLipCornerLeft", "mFaceLipCornerRight", FACE}, {"mFaceLipCornerRight", "mFaceLipCornerLeft", FACE, true}, + {"mFaceLipLowerLeft", "mFaceLipLowerRight", FACE}, {"mFaceLipLowerCenter", "", FACE}, {"mFaceLipLowerRight", "mFaceLipLowerLeft", FACE, true}, {"mFaceJaw", "", FACE}, //left hand @@ -169,22 +176,22 @@ public: {"mHandPinky1Left", "mHandPinky1Right", HANDS}, {"mHandPinky2Left", "mHandPinky2Right", HANDS}, {"mHandPinky3Left", "mHandPinky3Right", HANDS}, // right hand - {"mHandThumb1Right", "mHandThumb1Left", HANDS}, {"mHandThumb2Right", "mHandThumb2Left", HANDS}, {"mHandThumb3Right", "mHandThumb3Left", HANDS}, - {"mHandIndex1Right", "mHandIndex1Left", HANDS}, {"mHandIndex2Right", "mHandIndex2Left", HANDS}, {"mHandIndex3Right", "mHandIndex3Left", HANDS}, - {"mHandMiddle1Right", "mHandMiddle1Left", HANDS}, {"mHandMiddle2Right", "mHandMiddle2Left", HANDS}, {"mHandMiddle3Right", "mHandMiddle3Left", HANDS}, - {"mHandRing1Right", "mHandRing1Left", HANDS}, {"mHandRing2Right", "mHandRing2Left", HANDS}, {"mHandRing3Right", "mHandRing3Left", HANDS}, - {"mHandPinky1Right", "mHandPinky1Left", HANDS}, {"mHandPinky2Right", "mHandPinky2Left", HANDS}, {"mHandPinky3Right", "mHandPinky3Left", HANDS}, + {"mHandThumb1Right", "mHandThumb1Left", HANDS, true}, {"mHandThumb2Right", "mHandThumb2Left", HANDS, true}, {"mHandThumb3Right", "mHandThumb3Left", HANDS, true}, + {"mHandIndex1Right", "mHandIndex1Left", HANDS, true}, {"mHandIndex2Right", "mHandIndex2Left", HANDS, true}, {"mHandIndex3Right", "mHandIndex3Left", HANDS, true}, + {"mHandMiddle1Right", "mHandMiddle1Left", HANDS, true}, {"mHandMiddle2Right", "mHandMiddle2Left", HANDS, true}, {"mHandMiddle3Right", "mHandMiddle3Left", HANDS, true}, + {"mHandRing1Right", "mHandRing1Left", HANDS, true}, {"mHandRing2Right", "mHandRing2Left", HANDS, true}, {"mHandRing3Right", "mHandRing3Left", HANDS, true}, + {"mHandPinky1Right", "mHandPinky1Left", HANDS, true}, {"mHandPinky2Right", "mHandPinky2Left", HANDS, true}, {"mHandPinky3Right", "mHandPinky3Left", HANDS, true}, // tail and hind limbs {"mTail1", "", MISC}, {"mTail2", "", MISC}, {"mTail3", "", MISC}, {"mTail4", "", MISC}, {"mTail5", "", MISC}, {"mTail6", "", MISC}, {"mGroin", "", MISC}, {"mHindLimbsRoot", "", MISC}, {"mHindLimb1Left", "mHindLimb1Right", MISC}, {"mHindLimb2Left", "mHindLimb2Right", MISC}, {"mHindLimb3Left", "mHindLimb3Right", MISC}, {"mHindLimb4Left", "mHindLimb4Right", MISC}, - {"mHindLimb1Right", "mHindLimb1Left", MISC}, {"mHindLimb2Right", "mHindLimb2Left", MISC}, {"mHindLimb3Right", "mHindLimb3Left", MISC}, {"mHindLimb4Right", "mHindLimb4Left", MISC}, + {"mHindLimb1Right", "mHindLimb1Left", MISC, true}, {"mHindLimb2Right", "mHindLimb2Left", MISC, true}, {"mHindLimb3Right", "mHindLimb3Left", MISC, true}, {"mHindLimb4Right", "mHindLimb4Left", MISC, true}, // wings {"mWingsRoot", "", MISC}, {"mWing1Left", "mWing1Right", MISC}, {"mWing2Left", "mWing2Right", MISC}, {"mWing3Left", "mWing3Right", MISC}, {"mWing4Left", "mWing4Right", MISC}, {"mWing4FanLeft", "mWing4FanRight", MISC}, - {"mWing1Right", "mWing1Left", MISC}, {"mWing2Right", "mWing2Left", MISC}, {"mWing3Right", "mWing3Left", MISC}, {"mWing4Right", "mWing4Left", MISC}, {"mWing4FanRight", "mWing4FanLeft", MISC}, + {"mWing1Right", "mWing1Left", MISC, true}, {"mWing2Right", "mWing2Left", MISC, true}, {"mWing3Right", "mWing3Left", MISC, true}, {"mWing4Right", "mWing4Left", MISC, true}, {"mWing4FanRight", "mWing4FanLeft", MISC, true}, }; public: @@ -298,6 +305,19 @@ public: /// Any ancilliary action to be taken with the change to be made. void setJointScale(LLVOAvatar *avatar, const FSPoserJoint *joint, LLVector3 scale, E_BoneDeflectionStyles style); + /// + /// Reflects the joint with its opposite if it has one, or just mirror the rotation of itself. + /// + /// The avatar whose joint should flip left-right. + /// The joint to mirror rotation for. + void reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint); + + /// + /// Reflects every joint of the supplied avatar with its opposite if it has one, or mirrors the rotation of the joint if it does not have an opposite. + /// + /// The avatar whose pose should flip left-right. + void flipEntirePose(LLVOAvatar *avatar); + private: /// /// Translates a rotation vector from the UI to a Quaternion for the bone. @@ -315,15 +335,6 @@ public: /// The rotation to transform to matrix. /// The rotation vector. LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, LLQuaternion rotation); - - /// - /// Reflects the joint with its opposite if it has one, or just mirror itself. - /// - /// - /// - /// - /// - void reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint, E_BoneAxisTranslation translation, S32 negation); }; #endif // LL_FSPoserAnimator_H diff --git a/indra/newview/skins/default/xui/en/floater_poser.xml b/indra/newview/skins/default/xui/en/floater_poser.xml index 96dd19a8f8..90468de1af 100644 --- a/indra/newview/skins/default/xui/en/floater_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_poser.xml @@ -866,19 +866,34 @@ width="565"> image_overlay="Inv_Physics" image_unselected="Toolbar_Middle_Off" name="FlipPose_avatar" - tool_tip="Flip the current pose left/right" - width="28" + tool_tip="Flip the whole pose left/right" + width="23" top_delta="0" left_pad="1"> +