From fc0c59505141b69d5a7c8c433f2fea77359541c4 Mon Sep 17 00:00:00 2001 From: Angeldark Raymaker Date: Wed, 23 Oct 2024 23:17:30 +0100 Subject: [PATCH] FIRE-30873: Add hand presets in User pose directory --- indra/newview/fsfloaterposer.cpp | 150 ++++++++++++++++-- indra/newview/fsfloaterposer.h | 20 ++- .../skins/default/xui/en/floater_poser.xml | 125 +++++++++++---- 3 files changed, 248 insertions(+), 47 deletions(-) diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 171a29e27a..d92da2a346 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -48,6 +48,7 @@ static const std::string POSE_INTERNAL_FORMAT_FILE_MASK = "*.xml"; static const std::string POSE_INTERNAL_FORMAT_FILE_EXT = ".xml"; static const std::string POSE_EXTERNAL_FORMAT_FILE_EXT = ".bvh"; static const std::string POSE_SAVE_SUBDIRECTORY = "poses"; +static const std::string POSE_PRESETS_HANDS_SUBDIRECTORY = "poses\\hand_presets"; static const std::string XML_LIST_HEADER_STRING_PREFIX = "header_"; static const std::string XML_LIST_TITLE_STRING_PREFIX = "title_"; static const std::string XML_JOINT_TRANSFORM_STRING_PREFIX = "joint_transform_"; @@ -62,7 +63,8 @@ static const std::string POSER_AVATAR_TABGROUP_JOINTS = "joints_tabs"; static const std::string POSER_AVATAR_TAB_POSITION = "positionRotation_panel"; static const std::string POSER_AVATAR_TAB_BODY = "body_joints_panel"; static const std::string POSER_AVATAR_TAB_FACE = "face_joints_panel"; -static const std::string POSER_AVATAR_TAB_HANDS = "hands_joints_panel"; +static const std::string POSER_AVATAR_TAB_HANDS = "hands_tabs"; +static const std::string POSER_AVATAR_TAB_HANDJOINTS = "hands_joints_panel"; static const std::string POSER_AVATAR_TAB_MISC = "misc_joints_panel"; static const std::string POSER_AVATAR_TAB_VOLUMES = "collision_volumes_panel"; @@ -116,6 +118,7 @@ static const std::string POSER_AVATAR_SCROLLLIST_FACEJOINTS_NAME = "face_joi static const std::string POSER_AVATAR_SCROLLLIST_HANDJOINTS_NAME = "hand_joints_scroll"; static const std::string POSER_AVATAR_SCROLLLIST_MISCJOINTS_NAME = "misc_joints_scroll"; static const std::string POSER_AVATAR_SCROLLLIST_VOLUMES_NAME = "collision_volumes_scroll"; +static const std::string POSER_AVATAR_SCROLLLIST_HAND_PRESETS_NAME = "hand_presets_scroll"; FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) { @@ -146,6 +149,8 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) mCommitCallbackRegistrar.add("Poser.Save", boost::bind(&FSFloaterPoser::onClickPoseSave, this)); mCommitCallbackRegistrar.add("Pose.Menu", boost::bind(&FSFloaterPoser::onPoseMenuAction, this, _2)); mCommitCallbackRegistrar.add("Poser.BrowseCache", boost::bind(&FSFloaterPoser::onClickBrowsePoseCache, this)); + mCommitCallbackRegistrar.add("Poser.LoadLeftHand", boost::bind(&FSFloaterPoser::onClickLoadLeftHandPose, this)); + mCommitCallbackRegistrar.add("Poser.LoadRightHand", boost::bind(&FSFloaterPoser::onClickLoadRightHandPose, this)); mCommitCallbackRegistrar.add("Poser.FlipPose", boost::bind(&FSFloaterPoser::onClickFlipPose, this)); mCommitCallbackRegistrar.add("Poser.FlipJoint", boost::bind(&FSFloaterPoser::onClickFlipSelectedJoints, this)); @@ -261,6 +266,7 @@ void FSFloaterPoser::onOpen(const LLSD& key) refreshJointScrollListMembers(); onJointSelect(); onOpenSetAdvancedPanel(); + refreshPoseScroll(POSER_AVATAR_SCROLLLIST_HAND_PRESETS_NAME, POSE_PRESETS_HANDS_SUBDIRECTORY); } void FSFloaterPoser::onClose(bool app_quitting) @@ -274,15 +280,18 @@ void FSFloaterPoser::onClose(bool app_quitting) gSavedSettings.setBOOL(POSER_ALSOSAVEBVHFILE_SAVE_KEY, saveBvhCheckbox->getValue()); } -void FSFloaterPoser::refreshPosesScroll() +void FSFloaterPoser::refreshPoseScroll(std::string scrollListName, std::string subDirectory) { - LLScrollListCtrl *posesScrollList = getChild(POSER_AVATAR_SCROLLLIST_LOADSAVE_NAME); + if (scrollListName.empty() || subDirectory.empty()) + return; + + LLScrollListCtrl* posesScrollList = getChild(scrollListName); if (!posesScrollList) return; posesScrollList->clearRows(); - std::string dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses"); + std::string dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, subDirectory); std::string file; LLDirIterator dir_iter(dir, POSE_INTERNAL_FORMAT_FILE_MASK); while (dir_iter.next(file)) @@ -367,7 +376,7 @@ void FSFloaterPoser::onClickPoseSave() bool successfulSave = savePoseToXml(avatar, filename); if (successfulSave) { - refreshPosesScroll(); + refreshPoseScroll(POSER_AVATAR_SCROLLLIST_LOADSAVE_NAME, POSE_SAVE_SUBDIRECTORY); setUiSelectedAvatarSaveFileName(filename); // TODO: provide feedback for save @@ -682,6 +691,97 @@ void FSFloaterPoser::onPoseMenuAction(const LLSD ¶m) refreshJointScrollListMembers(); } +void FSFloaterPoser::onClickLoadLeftHandPose() +{ + // This is a double-click function: it needs to run twice within some amount of time to complete. + auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - _timeLastClickedJointReset; + _timeLastClickedJointReset = std::chrono::system_clock::now(); + if (timeIntervalSinceLastClick > _doubleClickInterval) + return; + + onClickLoadHandPose(false); +} + +void FSFloaterPoser::onClickLoadRightHandPose() +{ + // This is a double-click function: it needs to run twice within some amount of time to complete. + auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - _timeLastClickedJointReset; + _timeLastClickedJointReset = std::chrono::system_clock::now(); + if (timeIntervalSinceLastClick > _doubleClickInterval) + return; + + onClickLoadHandPose(true); +} + +void FSFloaterPoser::onClickLoadHandPose(bool isRightHand) +{ + LLScrollListCtrl* handPosesScrollList = getChild(POSER_AVATAR_SCROLLLIST_HAND_PRESETS_NAME); + if (!handPosesScrollList) + return; + + LLScrollListItem* item = handPosesScrollList->getFirstSelected(); + if (!item) + return; + + std::string poseName = item->getColumn(0)->getValue().asString(); + std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_PRESETS_HANDS_SUBDIRECTORY); + if (!gDirUtilp->fileExists(pathname)) + return; + + std::string fullPath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_PRESETS_HANDS_SUBDIRECTORY, poseName + POSE_INTERNAL_FORMAT_FILE_EXT); + + LLVOAvatar* avatar = getUiSelectedAvatar(); + if (!avatar) + return; + if (!_poserAnimator.isPosingAvatar(avatar)) + return; + + try + { + LLSD pose; + llifstream infile; + LLVector3 vec3; + + infile.open(fullPath); + if (!infile.is_open()) + return; + + while (!infile.eof()) + { + S32 lineCount = LLSDSerialize::fromXML(pose, infile); + if (lineCount == LLSDParser::PARSE_FAILURE) + { + LL_WARNS("Posing") << "Failed to parse loading a file for a hand: " << poseName << LL_ENDL; + return; + } + + for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr) + { + std::string const& name = itr->first; + LLSD const& control_map = itr->second; + + if (name.find("Hand") == std::string::npos) + continue; + if (isRightHand != (name.find("Left") == std::string::npos)) + continue; + + const FSPoserAnimator::FSPoserJoint* poserJoint = _poserAnimator.getPoserJointByName(name); + if (!poserJoint) + continue; + + vec3.setValue(control_map["rotation"]); + _poserAnimator.setJointRotation(avatar, poserJoint, vec3, NONE, SWAP_NOTHING, NEGATE_NOTHING); + } + } + } + catch (...) + { + LL_WARNS("Posing") << "Threw an exception trying to load a hand pose: " << poseName << LL_ENDL; + } + +} + void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, std::string poseFileName, E_LoadPoseMethods loadMethod) { std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); @@ -698,8 +798,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, std::string poseFileNam loadMethod == ROT_POS_AND_SCALES; bool loadPositions = loadMethod == POSITIONS || loadMethod == ROTATIONS_AND_POSITIONS || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROT_POS_AND_SCALES; - bool loadScales = loadMethod == SCALES || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROTATIONS_AND_SCALES || - loadMethod == ROT_POS_AND_SCALES; + bool loadScales = loadMethod == SCALES || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROTATIONS_AND_SCALES || + loadMethod == ROT_POS_AND_SCALES; + bool loadHandsOnly = loadMethod == HAND_RIGHT || loadMethod == HAND_LEFT; try { @@ -726,10 +827,21 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, std::string poseFileNam std::string const &name = itr->first; LLSD const &control_map = itr->second; + if (loadHandsOnly && name.find("Hand") == std::string::npos) + continue; + const FSPoserAnimator::FSPoserJoint *poserJoint = _poserAnimator.getPoserJointByName(name); if (!poserJoint) continue; + if (loadHandsOnly && control_map.has("rotation")) + { + vec3.setValue(control_map["rotation"]); + + _poserAnimator.setJointRotation(avatar, poserJoint, vec3, NONE, SWAP_NOTHING, NEGATE_NOTHING); + continue; + } + if (loadRotations && control_map.has("rotation")) { vec3.setValue(control_map["rotation"]); @@ -1016,7 +1128,7 @@ void FSFloaterPoser::onToggleLoadSavePanel() this->reshape(poserFloaterWidth, poserFloaterHeight); if (loadSavePanelExpanded) - refreshPosesScroll(); + refreshPoseScroll(POSER_AVATAR_SCROLLLIST_LOADSAVE_NAME, POSE_SAVE_SUBDIRECTORY); showOrHideAdvancedSaveOptions(); } @@ -1067,6 +1179,8 @@ void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool toggling if (togglingMirror || togglingSympathetic) deltaModeToggleButton->setValue(false); + + refreshTrackpadCursor(); } void FSFloaterPoser::onUndoLastRotation() @@ -1346,11 +1460,11 @@ std::vector FSFloaterPoser::getUiSelectedPoserJ { std::vector joints; - LLTabContainer *jointTabGroup = getChild(POSER_AVATAR_TABGROUP_JOINTS); - if (!jointTabGroup) + LLTabContainer *tabGroup = getChild(POSER_AVATAR_TABGROUP_JOINTS); + if (!tabGroup) return joints; - std::string activeTabName = jointTabGroup->getCurrentPanel()->getName(); + std::string activeTabName = tabGroup->getCurrentPanel()->getName(); if (activeTabName.empty()) return joints; @@ -1363,7 +1477,18 @@ std::vector FSFloaterPoser::getUiSelectedPoserJ else if (boost::iequals(activeTabName, POSER_AVATAR_TAB_FACE)) scrollListName = POSER_AVATAR_SCROLLLIST_FACEJOINTS_NAME; else if (boost::iequals(activeTabName, POSER_AVATAR_TAB_HANDS)) - scrollListName = POSER_AVATAR_SCROLLLIST_HANDJOINTS_NAME; + { + tabGroup = getChild(POSER_AVATAR_TAB_HANDS); + if (!tabGroup) + return joints; + + activeTabName = tabGroup->getCurrentPanel()->getName(); + if (activeTabName.empty()) + return joints; + + if (boost::iequals(activeTabName, POSER_AVATAR_TAB_HANDJOINTS)) + scrollListName = POSER_AVATAR_SCROLLLIST_HANDJOINTS_NAME; + } else if (boost::iequals(activeTabName, POSER_AVATAR_TAB_MISC)) scrollListName = POSER_AVATAR_SCROLLLIST_MISCJOINTS_NAME; else if (boost::iequals(activeTabName, POSER_AVATAR_TAB_VOLUMES)) @@ -2018,7 +2143,6 @@ uuid_vec_t FSFloaterPoser::getCurrentlyListedAvatarsAndAnimeshes() S32 FSFloaterPoser::getAvatarListIndexForUuid(LLUUID toFind) { - LLScrollListCtrl* avatarScrollList = getChild(POSER_AVATAR_SCROLLLIST_AVATARSELECTION); if (!avatarScrollList) return -1; diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 0fa12c42c1..288622e80e 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -38,11 +38,14 @@ typedef enum E_LoadPoseMethods { ROTATIONS = 1, POSITIONS = 2, - SCALES = 4, - ROTATIONS_AND_POSITIONS = 3, - ROTATIONS_AND_SCALES = 4, - POSITIONS_AND_SCALES = 5, - ROT_POS_AND_SCALES = 6 + SCALES = 3, + ROTATIONS_AND_POSITIONS = 4, + ROTATIONS_AND_SCALES = 5, + POSITIONS_AND_SCALES = 6, + ROT_POS_AND_SCALES = 7, + HAND_RIGHT = 8, + HAND_LEFT = 9, + FACE_ONLY = 10, } E_LoadPoseMethods; /// @@ -79,9 +82,9 @@ class FSFloaterPoser : public LLFloater const F32 normalTrackpadRangeInRads = F_PI; /// - /// Refreshes the pose load/save list. + /// Refreshes the supplied pose list from the supplued subdirectory. /// - void refreshPosesScroll(); + void refreshPoseScroll(std::string scrollListName, std::string subDirectory); /// /// (Dis)Enables all of the posing controls; such as when you can't pose for reasons. @@ -227,6 +230,9 @@ class FSFloaterPoser : public LLFloater void onPoseJointsReset(); void onOpenSetAdvancedPanel(); void onAdjustTrackpadSensitivity(); + void onClickLoadLeftHandPose(); + void onClickLoadRightHandPose(); + void onClickLoadHandPose(bool isRightHand); // UI Refreshments void refreshRotationSliders(); diff --git a/indra/newview/skins/default/xui/en/floater_poser.xml b/indra/newview/skins/default/xui/en/floater_poser.xml index ade91e8b45..05d17f4966 100644 --- a/indra/newview/skins/default/xui/en/floater_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_poser.xml @@ -521,38 +521,109 @@ width="565"> relative_width="0.9" /> - - - - - - + width="235"> + + + + + + + + + + + + + +