diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 00c754ed5c..55066dba15 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8080,17 +8080,6 @@ Value 0 - FSPoserAdvancedWindowState - - Comment - Whether the 'advanced' pane is shown when opening the Avatar/Animesh Poser. - Persist - 1 - Type - Boolean - Value - 0 - FSPoserSaveExternalFileAlso Comment @@ -8113,6 +8102,17 @@ Value 0 + FSPoserOnSaveConfirmOverwrite + + Comment + Whether to confirm overwriting a save file. + Persist + 1 + Type + Boolean + Value + 0 + FSPoserStopPosingWhenClosed Comment diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 9a54e1d7bf..5a5f04c7e6 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -61,6 +61,7 @@ constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpa constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed"; constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit"; constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso"; +constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite"; } // namespace /// @@ -83,11 +84,10 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) mCommitCallbackRegistrar.add("Poser.ToggleSympatheticChanges", [this](LLUICtrl*, const LLSD&) { onToggleSympatheticChange(); }); mCommitCallbackRegistrar.add("Poser.AdjustTrackPadSensitivity", [this](LLUICtrl*, const LLSD&) { onAdjustTrackpadSensitivity(); }); - mCommitCallbackRegistrar.add("Poser.PositionSet", [this](LLUICtrl*, const LLSD&) { onAvatarPositionSet(); }); + mCommitCallbackRegistrar.add("Poser.PositionSet", [this](LLUICtrl*, const LLSD&) { onPositionSet(); }); mCommitCallbackRegistrar.add("Poser.SetToTPose", [this](LLUICtrl*, const LLSD&) { onSetAvatarToTpose(); }); - mCommitCallbackRegistrar.add("Poser.Advanced.PositionSet", [this](LLUICtrl*, const LLSD&) { onAdvancedPositionSet(); }); - mCommitCallbackRegistrar.add("Poser.Advanced.ScaleSet", [this](LLUICtrl*, const LLSD&) { onAdvancedScaleSet(); }); + mCommitCallbackRegistrar.add("Poser.Advanced.ScaleSet", [this](LLUICtrl*, const LLSD&) { onScaleSet(); }); mCommitCallbackRegistrar.add("Poser.UndoLastPosition", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); }); mCommitCallbackRegistrar.add("Poser.RedoLastPosition", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); }); mCommitCallbackRegistrar.add("Poser.ResetJoint", [this](LLUICtrl*, const LLSD& data) { onResetJoint(data); }); @@ -104,13 +104,14 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) mCommitCallbackRegistrar.add("Poser.TogglePosingSelectedBones", [this](LLUICtrl*, const LLSD&) { onClickToggleSelectedBoneEnabled(); }); mCommitCallbackRegistrar.add("Poser.CommitSpinner", [this](LLUICtrl* spinner, const LLSD& data) { onCommitSpinner(spinner, data); }); + mCommitCallbackRegistrar.add("Poser.CommitSlider", [this](LLUICtrl* slider, const LLSD& data) { onCommitSlider(slider, data); }); mCommitCallbackRegistrar.add("Poser.Symmetrize", [this](LLUICtrl*, const LLSD& data) { onClickSymmetrize(data); }); } bool FSFloaterPoser::postBuild() { mAvatarTrackball = getChild("limb_rotation"); - mAvatarTrackball->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbTrackballChanged(); }); + mAvatarTrackball->setCommitCallback([this](LLUICtrl *, const LLSD &) { onTrackballChanged(); }); mJointsTabs = getChild("joints_tabs"); mJointsTabs->setCommitCallback( @@ -164,6 +165,10 @@ bool FSFloaterPoser::postBuild() mPosYSlider = getChild("av_position_leftright"); mPosZSlider = getChild("av_position_updown"); + mAdvRotXSlider = getChild("limb_pitch_slider"); + mAdvRotYSlider = getChild("limb_yaw_slider"); + mAdvRotZSlider = getChild("limb_roll_slider"); + mAdvPosXSlider = getChild("Advanced_Position_X"); mAdvPosYSlider = getChild("Advanced_Position_Y"); mAdvPosZSlider = getChild("Advanced_Position_Z"); @@ -178,6 +183,7 @@ bool FSFloaterPoser::postBuild() mBrowserFolderBtn = getChild("open_poseDir_button"); mLoadPosesBtn = getChild("load_poses_button"); mSavePosesBtn = getChild("save_poses_button"); + mSavePosesBtn->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onMouseLeaveSavePoseBtn(); }); mFlipPoseBtn = getChild("FlipPose_avatar"); mFlipJointBtn = getChild("FlipJoint_avatar"); @@ -188,6 +194,7 @@ bool FSFloaterPoser::postBuild() mToggleSympatheticRotationBtn = getChild("button_toggleSympatheticRotation"); mToggleDeltaModeBtn = getChild("delta_mode_toggle"); mRedoChangeBtn = getChild("button_redo_change"); + mUndoChangeBtn = getChild("undo_change"); mSetToTposeButton = getChild("set_t_pose_button"); mJointsParentPnl = getChild("joints_parent_panel"); @@ -252,15 +259,23 @@ void FSFloaterPoser::onFocusLost() LLEditMenuHandler::gEditMenuHandler = nullptr; } } + void FSFloaterPoser::enableVisualManipulators() { + if (!gAgentAvatarp || gAgentAvatarp.isNull()) + { + mToggleVisualManipulators->setToggleState(false); + return; + } + if (LLToolMgr::getInstance()->getCurrentToolset() != gCameraToolset) { mLastToolset = LLToolMgr::getInstance()->getCurrentToolset(); } + LLToolMgr::getInstance()->setCurrentToolset(gPoserToolset); LLToolMgr::getInstance()->getCurrentToolset()->selectTool(FSToolCompPose::getInstance()); - FSToolCompPose::getInstance()->setAvatar( gAgentAvatarp); + FSToolCompPose::getInstance()->setAvatar(gAgentAvatarp); } void FSFloaterPoser::disableVisualManipulators() @@ -347,16 +362,22 @@ void FSFloaterPoser::onPoseFileSelect() mPoseSaveNameEditor->setText(name); bool isDeltaSave = !poseFileStartsFromTeePose(name); - if (isDeltaSave) + if (isDeltaSave && hasString("LoadDiffLabel")) mLoadPosesBtn->setLabel(getString("LoadDiffLabel")); - else + else if (hasString("LoadPoseLabel")) mLoadPosesBtn->setLabel(getString("LoadPoseLabel")); } void FSFloaterPoser::onClickPoseSave() { std::string filename = mPoseSaveNameEditor->getValue().asString(); - if (filename.empty()) + if (filename.empty() && hasString("icon_save_failed_button")) + { + mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign()); + return; + } + + if (confirmFileOverwrite(filename)) return; LLVOAvatar* avatar = getUiSelectedAvatar(); @@ -372,8 +393,55 @@ void FSFloaterPoser::onClickPoseSave() if (getSavingToBvh()) savePoseToBvh(avatar, filename); - // TODO: provide feedback for save + if (hasString("icon_rotation_is_own_work")) + mSavePosesBtn->setImageOverlay(getString("icon_rotation_is_own_work"), mSavePosesBtn->getImageOverlayHAlign()); + + setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar)); } + else + { + if (hasString("icon_save_failed_button")) + mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign()); + } +} + +bool FSFloaterPoser::confirmFileOverwrite(std::string fileName) +{ + if (fileName.empty()) + return false; + + if (!gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY)) + return false; + + if (!hasString("icon_save_query")) + return false; + + if (mSavePosesBtn->getImageOverlay().notNull() && mSavePosesBtn->getImageOverlay()->getName() == getString("icon_save_query")) + return false; + + std::string fullSavePath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, fileName + POSE_INTERNAL_FORMAT_FILE_EXT); + + if (!gDirUtilp->fileExists(fullSavePath)) + return false; + + mSavePosesBtn->setImageOverlay(getString("icon_save_query"), mSavePosesBtn->getImageOverlayHAlign()); + if (hasString("OverWriteLabel")) + mSavePosesBtn->setLabel(getString("OverWriteLabel")); + + return true; +} + +void FSFloaterPoser::onMouseLeaveSavePoseBtn() +{ + if (hasString("icon_save_button")) + mSavePosesBtn->setImageOverlay(getString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign()); + + LLVOAvatar* avatar = getUiSelectedAvatar(); + if (!avatar) + return; + + setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar)); } void FSFloaterPoser::createUserPoseDirectoryIfNeeded() @@ -552,6 +620,7 @@ void FSFloaterPoser::onClickFlipSelectedJoints() } refreshRotationSlidersAndSpinners(); + enableOrDisableRedoAndUndoButton(); refreshTrackpadCursor(); } @@ -596,13 +665,11 @@ void FSFloaterPoser::onClickRecaptureSelectedBones() refreshRotationSlidersAndSpinners(); refreshTrackpadCursor(); refreshTextHighlightingOnJointScrollLists(); + enableOrDisableRedoAndUndoButton(); } -void FSFloaterPoser::updatePosedBones() -{ - auto selectedJoints = getUiSelectedPoserJoints(); - if (selectedJoints.size() < 1) - return; +void FSFloaterPoser::updatePosedBones(const std::string& jointName) +{ LLVOAvatar *avatar = getUiSelectedAvatar(); if (!avatar) return; @@ -610,18 +677,17 @@ void FSFloaterPoser::updatePosedBones() if (!mPoserAnimator.isPosingAvatar(avatar)) return; - for (auto item : selectedJoints) - { - bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); - if (!currentlyPosing) - continue; + const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName); + if (!poserJoint) + return; - mPoserAnimator.recaptureJointAsDelta(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName())); - } + mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, getUiSelectedBoneDeflectionStyle()); - setSavePosesButtonText(true); refreshRotationSlidersAndSpinners(); + refreshPositionSlidersAndSpinners(); + refreshScaleSlidersAndSpinners(); refreshTrackpadCursor(); + enableOrDisableRedoAndUndoButton(); refreshTextHighlightingOnJointScrollLists(); } @@ -633,11 +699,8 @@ void FSFloaterPoser::onClickBrowsePoseCache() gViewerWindow->getWindow()->openFile(pathname); } -void FSFloaterPoser::onClickSymmetrize(S32 ID) +void FSFloaterPoser::onClickSymmetrize(const S32 ID) { - if (notDoubleClicked()) - return; - LLVOAvatar* avatar = getUiSelectedAvatar(); if (!avatar) return; @@ -648,40 +711,35 @@ void FSFloaterPoser::onClickSymmetrize(S32 ID) mPoserAnimator.symmetrizeLeftToRightOrRightToLeft(avatar, ID == 2); refreshRotationSlidersAndSpinners(); + enableOrDisableRedoAndUndoButton(); refreshTrackpadCursor(); } -void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id) +void FSFloaterPoser::onCommitSpinner(const LLUICtrl* spinner, const S32 id) { if (!spinner) return; - auto activeTab = mJointsTabs->getCurrentPanel(); - if (!activeTab) - return; - - bool changingBodyPosition = activeTab == mPositionRotationPnl; - F32 value = (F32)spinner->getValue().asReal(); switch (id) { case 0: // av_position_updown_spinner { - mPosZSlider->setValue(value); - onAvatarPositionSet(); + mAdvPosZSpnr->setValue(value); + onPositionSet(); break; } case 1: // av_position_leftright { - mPosYSlider->setValue(value); - onAvatarPositionSet(); + mAdvPosYSpnr->setValue(value); + onPositionSet(); break; } case 2: // av_position_inout_spinner { - mPosXSlider->setValue(value); - onAvatarPositionSet(); + mAdvPosXSpnr->setValue(value); + onPositionSet(); break; } case 3: // trackpad_sensitivity_spinner @@ -690,50 +748,100 @@ void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id) break; } case 7: // adv_posx_spinner - { - if (changingBodyPosition) - mPosXSlider->setValue(value); - - mAdvPosXSlider->setValue(value); - onAdvancedPositionSet(); - break; - } case 8: // adv_posy_spinner - { - if (changingBodyPosition) - mPosYSlider->setValue(value); - - mAdvPosYSlider->setValue(value); - onAdvancedPositionSet(); - break; - } case 9: // adv_posz_spinner { - if (changingBodyPosition) - mPosZSlider->setValue(value); - - mAdvPosZSlider->setValue(value); - onAdvancedPositionSet(); + onPositionSet(); break; } case 10: // adv_scalex_spinner - { - mAdvScaleXSlider->setValue(value); - onAdvancedScaleSet(); - break; - } case 11: // adv_scaley_spinner - { - mAdvScaleYSlider->setValue(value); - onAdvancedScaleSet(); - break; - } case 12: // adv_scalez_spinner { - mAdvScaleZSlider->setValue(value); - onAdvancedScaleSet(); + onScaleSet(); break; } + + default: + LL_WARNS("Posing") << "onCommitSpinner passed invalid parameter: " << id << LL_ENDL; + break; + } +} + +void FSFloaterPoser::onCommitSlider(const LLUICtrl* slider, const S32 id) +{ + if (!slider) + return; + + F32 value = (F32)slider->getValue().asReal(); + + switch (id) + { + case 0: // av_position_updown + case 9: // Advanced_Position_Z + { + mAdvPosZSpnr->setValue(value); + onPositionSet(); + break; + } + + case 1: // av_position_leftright + case 8: // Advanced_Position_Y + { + mAdvPosYSpnr->setValue(value); + onPositionSet(); + break; + } + + case 2: // av_position_inout + case 7: // Advanced_Position_X + { + mAdvPosXSpnr->setValue(value); + onPositionSet(); + break; + } + + case 4: // limb_pitch_slider + { + mPitchSpnr->setValue(value); + onYawPitchRollChanged(); + break; + } + case 5: // limb_yaw_slider + { + mYawSpnr->setValue(value); + onYawPitchRollChanged(); + break; + } + case 6: // limb_roll_slider + { + mRollSpnr->setValue(value); + onYawPitchRollChanged(); + break; + } + + case 10: // Advanced_Scale_X + { + mScaleXSpnr->setValue(value); + onScaleSet(); + break; + } + case 11: // Advanced_Scale_Y + { + mScaleYSpnr->setValue(value); + onScaleSet(); + break; + } + case 12: // Advanced_Scale_Z + { + mScaleZSpnr->setValue(value); + onScaleSet(); + break; + } + + default: + LL_WARNS("Posing") << "onCommitSlider passed invalid parameter: " << id << LL_ENDL; + break; } } @@ -779,27 +887,13 @@ void FSFloaterPoser::onPoseMenuAction(const LLSD& param) setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar)); } -bool FSFloaterPoser::notDoubleClicked() -{ - auto timeIntervalSinceLastExecution = std::chrono::system_clock::now() - mTimeLastExecutedDoubleClickMethod; - mTimeLastExecutedDoubleClickMethod = std::chrono::system_clock::now(); - - return timeIntervalSinceLastExecution > mDoubleClickInterval; -} - void FSFloaterPoser::onClickLoadLeftHandPose() { - if (notDoubleClicked()) - return; - onClickLoadHandPose(false); } void FSFloaterPoser::onClickLoadRightHandPose() { - if (notDoubleClicked()) - return; - onClickLoadHandPose(true); } @@ -1295,19 +1389,15 @@ void FSFloaterPoser::onUndoLastChange() mPoserAnimator.undoLastJointChange(avatar, *item, getUiSelectedBoneDeflectionStyle()); } - enableOrDisableRedoButton(); + enableOrDisableRedoAndUndoButton(); refreshRotationSlidersAndSpinners(); - refreshTrackpadCursor(); refreshPositionSlidersAndSpinners(); - refreshAvatarPositionSlidersAndSpinners(); refreshScaleSlidersAndSpinners(); + refreshTrackpadCursor(); } void FSFloaterPoser::onSetAvatarToTpose() { - if (notDoubleClicked()) - return; - LLVOAvatar* avatar = getUiSelectedAvatar(); if (!avatar) return; @@ -1315,13 +1405,11 @@ void FSFloaterPoser::onSetAvatarToTpose() setSavePosesButtonText(false); mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar); refreshTextHighlightingOnJointScrollLists(); + enableOrDisableRedoAndUndoButton(); } void FSFloaterPoser::onResetJoint(const LLSD data) { - if (notDoubleClicked()) - return; - int resetType = data.asInteger(); LLVOAvatar* avatar = getUiSelectedAvatar(); @@ -1345,10 +1433,10 @@ void FSFloaterPoser::onResetJoint(const LLSD data) } refreshRotationSlidersAndSpinners(); - refreshTrackpadCursor(); - refreshAvatarPositionSlidersAndSpinners(); refreshPositionSlidersAndSpinners(); refreshScaleSlidersAndSpinners(); + refreshTrackpadCursor(); + enableOrDisableRedoAndUndoButton(); } void FSFloaterPoser::onRedoLastChange() @@ -1371,16 +1459,18 @@ void FSFloaterPoser::onRedoLastChange() mPoserAnimator.redoLastJointChange(avatar, *item, getUiSelectedBoneDeflectionStyle()); } - enableOrDisableRedoButton(); + enableOrDisableRedoAndUndoButton(); refreshRotationSlidersAndSpinners(); refreshTrackpadCursor(); refreshScaleSlidersAndSpinners(); refreshPositionSlidersAndSpinners(); - refreshAvatarPositionSlidersAndSpinners(); } -void FSFloaterPoser::enableOrDisableRedoButton() +void FSFloaterPoser::enableOrDisableRedoAndUndoButton() { + mRedoChangeBtn->setEnabled(false); + mUndoChangeBtn->setEnabled(false); + LLVOAvatar* avatar = getUiSelectedAvatar(); if (!avatar) return; @@ -1393,14 +1483,20 @@ void FSFloaterPoser::enableOrDisableRedoButton() return; bool shouldEnableRedoButton = false; + bool shouldEnableUndoButton = false; + for (auto item : selectedJoints) { bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item); - if (currentlyPosing) - shouldEnableRedoButton |= mPoserAnimator.canRedoJointChange(avatar, *item); + if (!currentlyPosing) + continue; + + shouldEnableRedoButton |= mPoserAnimator.canRedoOrUndoJointChange(avatar, *item); + shouldEnableUndoButton |= mPoserAnimator.canRedoOrUndoJointChange(avatar, *item, true); } mRedoChangeBtn->setEnabled(shouldEnableRedoButton); + mUndoChangeBtn->setEnabled(shouldEnableUndoButton); } void FSFloaterPoser::onToggleVisualManipulators() @@ -1504,6 +1600,7 @@ LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel * tabPanel) const LL_WARNS() << "Unknown tab panel: " << tabPanel << LL_ENDL; return nullptr; } + std::vector FSFloaterPoser::getUiSelectedPoserJoints() const { std::vector joints; @@ -1542,21 +1639,23 @@ std::vector FSFloaterPoser::getUiSelectedPoserJo joints.push_back(userData); } } - auto avatarp = getUiSelectedAvatar(); - if (avatarp) - { - if(joints.size() >= 1) - { - FSToolCompPose::getInstance()->setJoint( gAgentAvatarp->getJoint( JointKey::construct(joints[0]->jointName())) ); - } - else - { - FSToolCompPose::getInstance()->setJoint( nullptr ); - } - } + + updateManipWithFirstSelectedJoint(joints); + return joints; } +void FSFloaterPoser::updateManipWithFirstSelectedJoint(std::vector joints) +{ + if (!gAgentAvatarp || gAgentAvatarp.isNull()) + return; + + if (joints.size() >= 1) + FSToolCompPose::getInstance()->setJoint(gAgentAvatarp->getJoint(JointKey::construct(joints[0]->jointName()))); + else + FSToolCompPose::getInstance()->setJoint(nullptr); +} + E_RotationStyle FSFloaterPoser::getUiSelectedBoneRotationStyle(const std::string& jointName) const { if (jointName.empty()) @@ -1663,130 +1762,100 @@ LLVOAvatar* FSFloaterPoser::getAvatarByUuid(const LLUUID& avatarToFind) const return nullptr; } -void FSFloaterPoser::onAdvancedPositionSet() +void FSFloaterPoser::onPositionSet() { - F32 posX = mAdvPosXSlider->getValueF32(); - F32 posY = mAdvPosYSlider->getValueF32(); - F32 posZ = mAdvPosZSlider->getValueF32(); - - mAdvPosXSpnr->setValue(posX); + F32 posX = (F32)mAdvPosXSpnr->getValue().asReal(); + F32 posY = (F32)mAdvPosYSpnr->getValue().asReal(); + F32 posZ = (F32)mAdvPosZSpnr->getValue().asReal(); + mInOutSpnr->setValue(posX); - mAdvPosYSpnr->setValue(posY); mLeftRightSpnr->setValue(posY); - mAdvPosZSpnr->setValue(posZ); - mUpDownSpnr->setValue(posZ); - - setSelectedJointsPosition(posX, posY, posZ); - refreshAvatarPositionSlidersAndSpinners(); -} - -void FSFloaterPoser::onAdvancedScaleSet() -{ - F32 scX = mAdvScaleXSlider->getValueF32(); - F32 scY = mAdvScaleYSlider->getValueF32(); - F32 scZ = mAdvScaleZSlider->getValueF32(); - - mScaleXSpnr->setValue(scX); - mScaleYSpnr->setValue(scY); - mScaleZSpnr->setValue(scZ); - - setSelectedJointsScale(scX, scY, scZ); -} - -void FSFloaterPoser::onAvatarPositionSet() -{ - F32 posX = mPosXSlider->getValueF32(); - F32 posY = mPosYSlider->getValueF32(); - F32 posZ = mPosZSlider->getValueF32(); - - mAdvPosXSpnr->setValue(posX); - mInOutSpnr->setValue(posX); - mAdvPosYSpnr->setValue(posY); - mLeftRightSpnr->setValue(posY); - mAdvPosZSpnr->setValue(posZ); mUpDownSpnr->setValue(posZ); + mAdvPosXSlider->setValue(posX); + mAdvPosYSlider->setValue(posY); + mAdvPosZSlider->setValue(posZ); + mPosXSlider->setValue(posX); + mPosYSlider->setValue(posY); + mPosZSlider->setValue(posZ); setSelectedJointsPosition(posX, posY, posZ); refreshPositionSlidersAndSpinners(); + enableOrDisableRedoAndUndoButton(); } -void FSFloaterPoser::onLimbTrackballChanged() +void FSFloaterPoser::onScaleSet() { - LLVector3 trackPadPos, trackPadDeltaPos; - LLSD position = mAvatarTrackball->getValue(); + F32 scX = (F32)mScaleXSpnr->getValue().asReal(); + F32 scY = (F32)mScaleYSpnr->getValue().asReal(); + F32 scZ = (F32)mScaleZSpnr->getValue().asReal(); + + mAdvScaleXSlider->setValue(scX); + mAdvScaleYSlider->setValue(scY); + mAdvScaleZSlider->setValue(scZ); + + setSelectedJointsScale(scX, scY, scZ); + refreshScaleSlidersAndSpinners(); + enableOrDisableRedoAndUndoButton(); +} + +void FSFloaterPoser::onTrackballChanged() +{ + LLVector3 trackPadDeltaPos; LLSD deltaPosition = mAvatarTrackball->getValueDelta(); - if (position.isArray() && position.size() == 3 && deltaPosition.isArray() && deltaPosition.size() == 3) - { - trackPadPos.setValue(position); + if (deltaPosition.isArray() && deltaPosition.size() == 3) trackPadDeltaPos.setValue(deltaPosition); - } else return; F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f); - trackPadPos.mV[VX] *= trackPadSensitivity; - trackPadPos.mV[VY] *= trackPadSensitivity; + trackPadDeltaPos[VX] *= NormalTrackpadRangeInRads * trackPadSensitivity * RAD_TO_DEG; + trackPadDeltaPos[VY] *= NormalTrackpadRangeInRads * trackPadSensitivity * RAD_TO_DEG; + trackPadDeltaPos[VZ] *= NormalTrackpadRangeInRads * RAD_TO_DEG; - trackPadPos.mV[VX] = unWrapScale(trackPadPos.mV[VX]) * NormalTrackpadRangeInRads; - trackPadPos.mV[VY] = unWrapScale(trackPadPos.mV[VY]) * NormalTrackpadRangeInRads; - trackPadPos.mV[VZ] = unWrapScale(trackPadPos.mV[VZ]) * NormalTrackpadRangeInRads; + F32 axis1 = clipRange((F32)mYawSpnr->getValue().asReal() + trackPadDeltaPos[VX]); + F32 axis2 = (F32)mPitchSpnr->getValue().asReal() + trackPadDeltaPos[VY]; + F32 axis3 = (F32)mRollSpnr->getValue().asReal() + trackPadDeltaPos[VZ]; - trackPadDeltaPos[VX] *= NormalTrackpadRangeInRads * trackPadSensitivity; - trackPadDeltaPos[VY] *= NormalTrackpadRangeInRads * trackPadSensitivity; - trackPadDeltaPos[VZ] *= NormalTrackpadRangeInRads; + mYawSpnr->setValue(axis1); + mPitchSpnr->setValue(axis2); + mRollSpnr->setValue(axis3); - setSelectedJointsRotation(trackPadPos, trackPadDeltaPos); - - // WARNING! - // as tempting as it is to refactor the following to refreshRotationSliders(), don't. - // getRotationOfFirstSelectedJoint/setSelectedJointsRotation are - // not necessarily symmetric functions (see their remarks). - mYawSpnr->setValue(trackPadPos.mV[VX] *= RAD_TO_DEG); - mPitchSpnr->setValue(trackPadPos.mV[VY] *= RAD_TO_DEG); - mRollSpnr->setValue(trackPadPos.mV[VZ] *= RAD_TO_DEG); + onYawPitchRollChanged(true); } -F32 FSFloaterPoser::unWrapScale(F32 scale) +F32 FSFloaterPoser::clipRange(F32 value) { - if (scale > -1.f && scale < 1.f) - return scale; - - F32 result = fmodf(scale, 100.f); // to avoid time consuming while loops - while (result > 1) - result -= 2; - while (result < -1) - result += 2; + F32 result = fmodf(value, 3600.f); // to avoid time consuming while loops + while (result > 180.f) + result -= 360.f; + while (result < -180.f) + result += 360.f; return result; } -void FSFloaterPoser::onYawPitchRollChanged() +void FSFloaterPoser::onYawPitchRollChanged(bool skipUpdateTrackpad) { LLVector3 absoluteRotation, deltaRotation; - absoluteRotation.mV[VX] = (F32)mYawSpnr->getValue().asReal() * DEG_TO_RAD; - absoluteRotation.mV[VY] = (F32)mPitchSpnr->getValue().asReal() * DEG_TO_RAD; - absoluteRotation.mV[VZ] = (F32)mRollSpnr->getValue().asReal() * DEG_TO_RAD; + absoluteRotation.mV[VX] = (F32)mYawSpnr->getValue().asReal(); + absoluteRotation.mV[VY] = (F32)mPitchSpnr->getValue().asReal(); + absoluteRotation.mV[VZ] = (F32)mRollSpnr->getValue().asReal(); + mAdvRotXSlider->setValue(absoluteRotation.mV[VY]); + mAdvRotYSlider->setValue(absoluteRotation.mV[VX]); + mAdvRotZSlider->setValue(absoluteRotation.mV[VZ]); + + absoluteRotation *= DEG_TO_RAD; deltaRotation = absoluteRotation - mLastSliderRotation; mLastSliderRotation = absoluteRotation; setSelectedJointsRotation(absoluteRotation, deltaRotation); + enableOrDisableRedoAndUndoButton(); - // WARNING! - // as tempting as it is to refactor the following to refreshTrackpadCursor(), don't. - // getRotationOfFirstSelectedJoint/setSelectedJointsRotation are - // not necessarily symmetric functions (see their remarks). - F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f); - absoluteRotation.mV[VX] /= trackPadSensitivity; - absoluteRotation.mV[VY] /= trackPadSensitivity; - - absoluteRotation.mV[VX] /= NormalTrackpadRangeInRads; - absoluteRotation.mV[VY] /= NormalTrackpadRangeInRads; - absoluteRotation.mV[VZ] /= NormalTrackpadRangeInRads; - - mAvatarTrackball->setValue(absoluteRotation.getValue()); + if (!skipUpdateTrackpad) + refreshTrackpadCursor(); } void FSFloaterPoser::onAdjustTrackpadSensitivity() @@ -1796,38 +1865,14 @@ void FSFloaterPoser::onAdjustTrackpadSensitivity() void FSFloaterPoser::refreshTrackpadCursor() { - F32 axis1 = (F32)mYawSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads; - F32 axis2 = (F32)mPitchSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads; + F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f); + F32 axis1 = (F32)mYawSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads / trackPadSensitivity; + F32 axis2 = (F32)mPitchSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads / trackPadSensitivity; F32 axis3 = (F32)mRollSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads; - F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f); - axis1 /= trackPadSensitivity; - axis2 /= trackPadSensitivity; - mAvatarTrackball->setValue(axis1, axis2, axis3); } -/// -/// This only sets the position sliders of the 'basic' view (not the advanced sliders). -/// -void FSFloaterPoser::refreshAvatarPositionSlidersAndSpinners() -{ - auto activeTab = mJointsTabs->getCurrentPanel(); - if (!activeTab) - return; - - if (activeTab != mPositionRotationPnl) - return; // if the active tab isn't the av position one, don't set anything. - - LLVector3 position = getPositionOfFirstSelectedJoint(); - mPosXSlider->setValue(position.mV[VX]); - mInOutSpnr->setValue(position.mV[VX]); - mPosYSlider->setValue(position.mV[VY]); - mLeftRightSpnr->setValue(position.mV[VY]); - mPosZSlider->setValue(position.mV[VZ]); - mUpDownSpnr->setValue(position.mV[VZ]); -} - void FSFloaterPoser::refreshRotationSlidersAndSpinners() { LLVector3 rotation = getRotationOfFirstSelectedJoint(); @@ -1836,6 +1881,9 @@ void FSFloaterPoser::refreshRotationSlidersAndSpinners() mYawSpnr->setValue(rotation.mV[VX] *= RAD_TO_DEG); mPitchSpnr->setValue(rotation.mV[VY] *= RAD_TO_DEG); mRollSpnr->setValue(rotation.mV[VZ] *= RAD_TO_DEG); + mAdvRotXSlider->setValue(rotation.mV[VY]); + mAdvRotYSlider->setValue(rotation.mV[VX]); + mAdvRotZSlider->setValue(rotation.mV[VZ]); } void FSFloaterPoser::refreshPositionSlidersAndSpinners() @@ -1848,6 +1896,20 @@ void FSFloaterPoser::refreshPositionSlidersAndSpinners() mAdvPosYSpnr->setValue(position.mV[VY]); mAdvPosZSlider->setValue(position.mV[VZ]); mAdvPosZSpnr->setValue(position.mV[VZ]); + + auto activeTab = mJointsTabs->getCurrentPanel(); + if (!activeTab) + return; + + if (activeTab != mPositionRotationPnl) + return; // if the active tab isn't the av position one, don't set anything. + + mPosXSlider->setValue(position.mV[VX]); + mInOutSpnr->setValue(position.mV[VX]); + mPosYSlider->setValue(position.mV[VY]); + mLeftRightSpnr->setValue(position.mV[VY]); + mPosZSlider->setValue(position.mV[VZ]); + mUpDownSpnr->setValue(position.mV[VZ]); } void FSFloaterPoser::refreshScaleSlidersAndSpinners() @@ -2003,11 +2065,10 @@ LLVector3 FSFloaterPoser::getScaleOfFirstSelectedJoint() const void FSFloaterPoser::onJointTabSelect() { - refreshAvatarPositionSlidersAndSpinners(); + refreshPositionSlidersAndSpinners(); refreshRotationSlidersAndSpinners(); refreshTrackpadCursor(); - enableOrDisableRedoButton(); - refreshPositionSlidersAndSpinners(); + enableOrDisableRedoAndUndoButton(); refreshScaleSlidersAndSpinners(); onClickSetBaseRotZero(); } @@ -2276,7 +2337,8 @@ void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists() void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff) { - setAsSaveDiff ? mSavePosesBtn->setLabel("Save Diff") : mSavePosesBtn->setLabel("Save Pose"); + if (hasString("SavePoseLabel") && hasString("SaveDiffLabel")) + setAsSaveDiff ? mSavePosesBtn->setLabel(getString("SaveDiffLabel")) : mSavePosesBtn->setLabel(getString("SavePoseLabel")); } void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar) @@ -2571,8 +2633,8 @@ S32 FSFloaterPoser::getBvhJointNegation(const std::string& jointName) const return result; } - bool FSFloaterPoser::getWhetherToResetBaseRotationOnEdit() { return gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); } + void FSFloaterPoser::onClickSetBaseRotZero() { mAlsoSaveBvhCbx->setEnabled(getWhetherToResetBaseRotationOnEdit()); } bool FSFloaterPoser::getSavingToBvh() diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 3d21953b05..622577b0de 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -78,13 +78,13 @@ class FSFloaterPoser : public LLFloater, public LLEditMenuHandler { friend class LLFloaterReg; FSFloaterPoser(const LLSD &key); -public: - void updatePosedBones(); +public: + void updatePosedBones(const std::string& jointName); void selectJointByName(const std::string& jointName); - void undo() override { onUndoLastChange(); }; - bool canUndo() const override { return true; } - void redo() override { onRedoLastChange(); }; - bool canRedo() const override { return true; } + void undo() override { onUndoLastChange(); }; + bool canUndo() const override { return true; } + void redo() override { onRedoLastChange(); }; + bool canRedo() const override { return true; } private: bool postBuild() override; void onOpen(const LLSD& key) override; @@ -132,6 +132,12 @@ public: /// The selected joints std::vector getUiSelectedPoserJoints() const; + /// + /// Updates the visual with the first selected joint from the supplied collection, if any. + /// + /// The collection of selected joints. + static void updateManipWithFirstSelectedJoint(std::vector joints); + /// /// Gets a detectable avatar by its UUID. /// @@ -214,6 +220,7 @@ public: void createUserPoseDirectoryIfNeeded(); void onToggleLoadSavePanel(); void onClickPoseSave(); + void onMouseLeaveSavePoseBtn(); void onPoseFileSelect(); bool savePoseToXml(LLVOAvatar* avatar, const std::string& posePath); bool savePoseToBvh(LLVOAvatar* avatar, const std::string& posePath); @@ -223,31 +230,30 @@ public: bool poseFileStartsFromTeePose(const std::string& poseFileName); void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); void setUiSelectedAvatarSaveFileName(const std::string& saveFileName); + bool confirmFileOverwrite(std::string fileName); + void startPosingSelf(); + void stopPosingAllAvatars(); // visual manipulators control void enableVisualManipulators(); void disableVisualManipulators(); - // UI Event Handlers: + // UI Event Handlers void onAvatarsRefresh(); void onAvatarSelect(); void onJointTabSelect(); void onToggleMirrorChange(); void onToggleSympatheticChange(); - void onToggleVisualManipulators(); + void onToggleVisualManipulators(); void setRotationChangeButtons(bool mirror, bool sympathetic); void onUndoLastChange(); void onRedoLastChange(); void onResetJoint(const LLSD data); void onSetAvatarToTpose(); - void enableOrDisableRedoButton(); void onPoseStartStop(); - void startPosingSelf(); - void stopPosingAllAvatars(); - void onLimbTrackballChanged(); - void onYawPitchRollChanged(); - void onAvatarPositionSet(); - void onAdvancedPositionSet(); - void onAdvancedScaleSet(); + void onTrackballChanged(); + void onYawPitchRollChanged(bool skipUpdateTrackpad = false); + void onPositionSet(); + void onScaleSet(); void onClickToggleSelectedBoneEnabled(); void onClickRecaptureSelectedBones(); void onClickFlipPose(); @@ -257,15 +263,16 @@ public: void onClickLoadRightHandPose(); void onClickLoadHandPose(bool isRightHand); void onClickSetBaseRotZero(); - void onCommitSpinner(LLUICtrl* spinner, S32 ID); - void onClickSymmetrize(S32 ID); + void onCommitSpinner(const LLUICtrl* spinner, const S32 ID); + void onCommitSlider(const LLUICtrl* slider, const S32 id); + void onClickSymmetrize(const S32 ID); // UI Refreshments void refreshRotationSlidersAndSpinners(); - void refreshAvatarPositionSlidersAndSpinners(); - void refreshTrackpadCursor(); void refreshPositionSlidersAndSpinners(); void refreshScaleSlidersAndSpinners(); + void refreshTrackpadCursor(); + void enableOrDisableRedoAndUndoButton(); /// /// Determines if we have permission to animate the supplied avatar. @@ -341,12 +348,6 @@ public: /// The avatar to whom the list is relevant. void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar); - /// - /// Determines if the user has run this method twice within mDoubleClickInterval. - /// - /// true if this method has executed since mDoubleClickInterval seconds ago, otherwise false. - bool notDoubleClicked(); - /// /// Gets whether the user wishes to reset the base-rotation to zero when they start editing a joint. /// @@ -426,26 +427,15 @@ public: std::string static vec3ToXYZString(const LLVector3& val); /// - /// The time when the last click of a button was made. - /// Utilized for controls needing a 'double click do' function. + /// Performs an angle module of the supplied value to between -180 & 180 (degrees). /// - std::chrono::system_clock::time_point mTimeLastExecutedDoubleClickMethod = std::chrono::system_clock::now(); - - /// - /// The constant time interval, in seconds, a user must execute the notDoubleClicked twice to successfully 'double-click' a button. - /// - std::chrono::duration const mDoubleClickInterval = std::chrono::duration(0.3); - - /// - /// Unwraps a normalized value from the trackball to a slider value. - /// - /// The scale value from the trackball. - /// A value appropriate for fitting a slider. + /// The value to modulo. + /// The modulo value. /// - /// If the trackpad is in 'infinite scroll' mode, it can produce normalized-values outside the range of the sliders. - /// This method ensures whatever value the trackpad produces, they work with the sliders. + /// If the trackpad is in 'infinite scroll' mode, it can produce normalized-values outside the range of the spinners. + /// This method ensures whatever value the trackpad produces, they work with the spinners. /// - static F32 unWrapScale(F32 scale); + static F32 clipRange(F32 value); LLToolset* mLastToolset{ nullptr }; LLTool* mJointRotTool{ nullptr }; @@ -458,6 +448,9 @@ public: LLSliderCtrl* mPosXSlider{ nullptr }; LLSliderCtrl* mPosYSlider{ nullptr }; LLSliderCtrl* mPosZSlider{ nullptr }; + LLSliderCtrl* mAdvRotXSlider{ nullptr }; + LLSliderCtrl* mAdvRotYSlider{ nullptr }; + LLSliderCtrl* mAdvRotZSlider{ nullptr }; LLSliderCtrl* mAdvPosXSlider{ nullptr }; LLSliderCtrl* mAdvPosYSlider{ nullptr }; LLSliderCtrl* mAdvPosZSlider{ nullptr }; @@ -492,6 +485,7 @@ public: LLButton* mToggleSympatheticRotationBtn{ nullptr }; LLButton* mToggleDeltaModeBtn{ nullptr }; LLButton* mRedoChangeBtn{ nullptr }; + LLButton* mUndoChangeBtn{ nullptr }; LLButton* mSetToTposeButton{ nullptr }; LLButton* mBtnJointRotate{ nullptr }; diff --git a/indra/newview/fsjointpose.cpp b/indra/newview/fsjointpose.cpp index 53a6b2869e..608e737aba 100644 --- a/indra/newview/fsjointpose.cpp +++ b/indra/newview/fsjointpose.cpp @@ -144,21 +144,15 @@ void FSJointPose::recaptureJoint() addStateToUndo(FSJointState(mCurrentState)); mCurrentState = FSJointState(joint); } + void FSJointPose::recaptureJointAsDelta() { - if (mIsCollisionVolume) - { - return; - } - LLJoint* joint = mJointState->getJoint(); if (!joint) - { return; - } - - addStateToUndo(mCurrentState); - mCurrentState = FSJointState(joint); + + addStateToUndo(FSJointState(mCurrentState)); + mCurrentState.updateFromJoint(joint); } void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint) @@ -209,6 +203,9 @@ void FSJointPose::zeroBaseRotation() if (mIsCollisionVolume) return; + if (!isBaseRotationZero()) + purgeUndoQueue(); + mCurrentState.zeroBaseRotation(); } @@ -219,3 +216,22 @@ bool FSJointPose::isBaseRotationZero() const return mCurrentState.baseRotationIsZero(); } + +void FSJointPose::purgeUndoQueue() +{ + mUndoneJointStatesIndex = 0; + mLastSetJointStates.clear(); +} + +bool FSJointPose::canPerformUndo() const +{ + switch (mLastSetJointStates.size()) + { + case 0: // nothing to undo + return false; + case 1: // there is only one change + return true; + default: // current state is not the bottom of the deque + return mUndoneJointStatesIndex != (mLastSetJointStates.size() - 1); + } +} diff --git a/indra/newview/fsjointpose.h b/indra/newview/fsjointpose.h index fe2d200bd1..05c96475ff 100644 --- a/indra/newview/fsjointpose.h +++ b/indra/newview/fsjointpose.h @@ -108,6 +108,12 @@ class FSJointPose /// True if the represented joint is zero, otherwise false. bool isBaseRotationZero() const; + /// + /// Gets whether an undo of this joint may be performed. + /// + /// true if the joint may have a undo applied, otherwise false. + bool canPerformUndo() const; + /// /// Gets whether a redo of this joint may be performed. /// @@ -143,11 +149,16 @@ class FSJointPose /// Resets the beginning properties of the joint this represents. /// void recaptureJoint(); + /// /// Recalculates the delta reltive to the base for a new rotation. /// void recaptureJointAsDelta(); + /// + /// Clears the undo/redo deque. + /// + void purgeUndoQueue(); /// /// Reverts the position/rotation/scale to their values when the animation begun. @@ -185,7 +196,6 @@ class FSJointPose inv_base.conjugate(); mDeltaRotation = newRotation * inv_base; }; - void reflectRotation() { @@ -215,6 +225,18 @@ class FSJointPose joint->setScale(mBaseScale); } + void updateFromJoint(LLJoint* joint) + { + if (!joint) + return; + + LLQuaternion invRot = mBaseRotation; + invRot.conjugate(); + mRotation = joint->getRotation() * invRot; + mPosition.set(joint->getPosition() - mBasePosition); + mScale.set(joint->getScale() - mBaseScale); + } + private: FSJointState(FSJointState* state) { diff --git a/indra/newview/fsmaniprotatejoint.cpp b/indra/newview/fsmaniprotatejoint.cpp index 8e5c4466da..da1bd33a90 100644 --- a/indra/newview/fsmaniprotatejoint.cpp +++ b/indra/newview/fsmaniprotatejoint.cpp @@ -1012,9 +1012,9 @@ bool FSManipRotateJoint::handleMouseUp(S32 x, S32 y, MASK mask) if (hasMouseCapture()) { // Update the UI, by causing it to read back the position of the selected joints and aply those relative to the base rot - if (poser) + if (poser && mJoint) { - poser->updatePosedBones(); + poser->updatePosedBones(mJoint->getName()); } // Release mouse diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 39938d4490..62d9030c85 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -138,7 +138,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, oppositeJointPose->setPublicScale(LLVector3()); } -bool FSPoserAnimator::canRedoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint) +bool FSPoserAnimator::canRedoOrUndoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, bool canUndo) { if (!isAvatarSafeToUse(avatar)) return false; @@ -154,6 +154,9 @@ bool FSPoserAnimator::canRedoJointChange(LLVOAvatar* avatar, const FSPoserJoint& if (!jointPose) return false; + if (canUndo) + return jointPose->canPerformUndo(); + return jointPose->canPerformRedo(); } @@ -303,7 +306,7 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar) if (!posingMotion) return; - posingMotion->setAllRotationsToZero(); + posingMotion->setAllRotationsToZeroAndClearUndo(); } void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) @@ -322,7 +325,8 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi jointPose->recaptureJoint(); setPosingAvatarJoint(avatar, joint, true); } -void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) + +void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style) { if (!isAvatarSafeToUse(avatar)) return; @@ -331,12 +335,34 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi if (!posingMotion) return; - FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName()); if (!jointPose) return; jointPose->recaptureJointAsDelta(); - setPosingAvatarJoint(avatar, joint, true); + + if (style == NONE || style == DELTAMODE) + return; + + FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName()); + if (!oppositeJointPose) + return; + + switch (style) + { + case SYMPATHETIC: + case SYMPATHETIC_DELTA: + oppositeJointPose->cloneRotationFrom(jointPose); + break; + + case MIRROR: + case MIRROR_DELTA: + oppositeJointPose->mirrorRotationFrom(jointPose); + break; + + default: + break; + } } LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 045558288a..8056ba7785 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -417,8 +417,9 @@ public: /// /// The avatar having the joint to which we refer. /// The joint to query. - /// True if a redo action is available, otherwise false. - bool canRedoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint); + /// Supply true to query if we can perform an Undo, otherwise query redo. + /// True if an undo or redo action is available, otherwise false. + bool canRedoOrUndoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, bool canUndo = false); /// /// Re-does the last undone change (rotation, position or scale) to the supplied PoserJoint. @@ -503,20 +504,27 @@ public: /// /// Symmetrizes the rotations of the joints from one side of the supplied avatar to the other. /// - /// The avatar whose joints to symmetrizet. + /// The avatar to symmetrize. /// Whether to symmetrize rotations from right to left, otherwise symmetrize left to right. void symmetrizeLeftToRightOrRightToLeft(LLVOAvatar* avatar, bool rightToLeft); /// /// Recaptures the rotation, position and scale state of the supplied joint for the supplied avatar. - /// AsDelta variant retians the original base and creates a delta relative to it. + /// AsDelta variant retains the original base and creates a delta relative to it. /// /// The avatar whose joint is to be recaptured. /// The joint to recapture. /// The axial translation form the supplied joint. /// The style of negation to apply to the recapture. void recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation); - void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation); + + /// + /// Recaptures any change in joint state. + /// + /// The avatar whose joint is to be recaptured. + /// The joint to recapture. + /// Any ancilliary action to be taken with the change to be made. + void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style); /// /// Sets all of the joint rotations of the supplied avatar to zero. @@ -525,14 +533,11 @@ public: void setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar); /// - /// Determines if the kind of save to perform should be a 'delta' save, or a complete save. + /// Determines if the supplied joint has a base rotation of zero. /// - /// The avatar whose pose-rotations are being considered for saving. - /// True if the save should save only 'deltas' to the rotation, otherwise false. - /// - /// A save of the rotation 'deltas' facilitates a user saving their changes to an existing animation. - /// Thus the save represents 'nothing other than the changes the user made', to some other pose which they may have limited rights to. - /// + /// The avatar owning the supplied joint. + /// The joint to query. + /// True if the supplied joint has a 'base' rotation of zero (thus user-supplied change only), otherwise false. bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const; /// diff --git a/indra/newview/fsposingmotion.cpp b/indra/newview/fsposingmotion.cpp index fc418dd911..841ab3a452 100644 --- a/indra/newview/fsposingmotion.cpp +++ b/indra/newview/fsposingmotion.cpp @@ -255,7 +255,7 @@ bool FSPosingMotion::allStartingRotationsAreZero() const return true; } -void FSPosingMotion::setAllRotationsToZero() +void FSPosingMotion::setAllRotationsToZeroAndClearUndo() { for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) { diff --git a/indra/newview/fsposingmotion.h b/indra/newview/fsposingmotion.h index 71c7d41063..9d862ffbef 100644 --- a/indra/newview/fsposingmotion.h +++ b/indra/newview/fsposingmotion.h @@ -119,9 +119,9 @@ public: bool allStartingRotationsAreZero() const; /// - /// Sets all of the non-Collision Volume rotations to zero. + /// Sets all of the non-Collision Volume base-and-delta rotations to zero, and clears the undo/redo queue. /// - void setAllRotationsToZero(); + void setAllRotationsToZeroAndClearUndo(); private: /// diff --git a/indra/newview/skins/default/xui/en/floater_fs_poser.xml b/indra/newview/skins/default/xui/en/floater_fs_poser.xml index 2a12eb19a9..f365a82e90 100644 --- a/indra/newview/skins/default/xui/en/floater_fs_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_fs_poser.xml @@ -11,6 +11,9 @@ width="430"> Inv_Object Check_Mark + Icon_Dock_Foreground + Parcel_Exp_Color + Info tool_tip="Move the selected avatar up or down" can_edit_text="true"> + function="Poser.CommitSlider" + parameter="0"/> tool_tip="Move the selected avatar left or right" can_edit_text="true"> + function="Poser.CommitSlider" + parameter="1"/> tool_tip="Move the selected avatar in or out" can_edit_text="true"> + function="Poser.CommitSlider" + parameter="2"/> layout="topleft" label="Set Left" name="button_loadHandPoseLeft" - tool_tip="Double click to set your left hand to the selected preset" + tool_tip="Click to set your left hand to the selected preset" top_delta="238" left_pad="1" left="2" @@ -651,7 +660,7 @@ width="430"> layout="topleft" label="Set Right" name="button_loadHandPoseRight" - tool_tip="Double click to set your right hand to the selected preset" + tool_tip="Click to set your right hand to the selected preset" left_pad="2" top_delta="0" width="85" > @@ -819,7 +828,7 @@ width="430"> label="Set to T-Pose" image_unselected="Toolbar_Middle_Off" name="set_t_pose_button" - tool_tip="Double-click to set the selected avatar to a 'T-Pose'" + tool_tip="Click to set the selected avatar to a 'T-Pose'" width="172"> @@ -922,6 +931,16 @@ width="430"> tool_tip="When you save your pose, also write a BVH file, which can be uploaded via the 'Build > Upload > Animation' to pose yourself or others in-world. This needs joints to reset their 'base' to zero, because BVH requires original work." top_pad="2" width="134" /> +