FIRE-30873: Add flip-joint button and refactor flipping

master
Angeldark Raymaker 2024-09-15 21:26:47 +01:00
parent 9b08290bde
commit 7e761d5b0e
5 changed files with 145 additions and 57 deletions

View File

@ -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<FSPoserAnimator::FSPoserJoint>::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<LLButton>(POSER_AVATAR_PANEL_BUTTON_FLIPJOINT_NAME);
if (someButton)
someButton->setEnabled(enable);
someButton = getChild<LLButton>(POSER_AVATAR_PANEL_BUTTON_RECAPTURE_NAME);
if (someButton)
someButton->setEnabled(enable);

View File

@ -192,6 +192,7 @@ class FSFloaterPoser : public LLFloater
void onClickToggleSelectedBoneEnabled();
void onClickRecaptureSelectedBones();
void onClickFlipPose();
void onClickFlipSelectedJoints();
void onPoseResetMenuAction(const LLSD &param);
// UI Refreshments

View File

@ -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)

View File

@ -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;
/// <summary>
@ -91,12 +90,14 @@ public:
/// <summary>
/// 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#
/// </summary>
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:
/// <summary>
/// Gets the name of the joint.
@ -113,6 +114,11 @@ public:
/// </summary>
E_BoneTypes boneType() const { return _boneList; }
/// <summary>
/// Gets whether when mirroring the entire body, should this joint flip its counterpart.
/// </summary>
bool dontFlipOnMirror() const { return _dontFlipOnMirror; }
/// <summary>
/// Creates a new instance of a PoserJoint.
/// </summary>
@ -123,11 +129,12 @@ public:
/// </param>
/// <param name="b">The opposite joint name, if any. Also expected to be a well-known name.</param>
/// <param name="c">The </param>
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:
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
void setJointScale(LLVOAvatar *avatar, const FSPoserJoint *joint, LLVector3 scale, E_BoneDeflectionStyles style);
/// <summary>
/// Reflects the joint with its opposite if it has one, or just mirror the rotation of itself.
/// </summary>
/// <param name="avatar">The avatar whose joint should flip left-right.</param>
/// <param name="joint">The joint to mirror rotation for.</param>
void reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint);
/// <summary>
/// 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.
/// </summary>
/// <param name="avatar">The avatar whose pose should flip left-right.</param>
void flipEntirePose(LLVOAvatar *avatar);
private:
/// <summary>
/// Translates a rotation vector from the UI to a Quaternion for the bone.
@ -315,15 +335,6 @@ public:
/// <param name="rotation">The rotation to transform to matrix.</param>
/// <returns>The rotation vector.</returns>
LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, LLQuaternion rotation);
/// <summary>
/// Reflects the joint with its opposite if it has one, or just mirror itself.
/// </summary>
/// <param name="avatar"></param>
/// <param name="joint"></param>
/// <param name="translation"></param>
/// <param name="negation"></param>
void reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint, E_BoneAxisTranslation translation, S32 negation);
};
#endif // LL_FSPoserAnimator_H

View File

@ -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">
<button.commit_callback
function="Poser.FlipPose"/>
</button>
<button
height="21"
follows="top|left"
layout="topleft"
label=""
image_overlay="Sync_Progress_1"
image_unselected="Toolbar_Middle_Off"
name="FlipJoint_avatar"
tool_tip="Mirror the selected joint(s) left/right"
width="23"
top_delta="0"
left_pad="1">
<button.commit_callback
function="Poser.FlipJoint"/>
</button>
<button
follows="left|top"
height="21"
layout="topleft"
enabled="true"
label="Recapture"
label="Recap"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
@ -886,7 +901,7 @@ width="565">
left_pad="1"
top_delta="0"
tool_tip="If the selected part is OFF, this Recaptures what that body part is doing right now"
width="65" >
width="45" >
<button.commit_callback
function="Poser.RecaptureSelectedBones"/>
</button>