diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e9165396be..3a2d755cc6 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8056,6 +8056,17 @@ Value 0 + FSPoserSaveBvhFileAlso + + Comment + Whether to save a BVH file as well when saving a pose. + Persist + 1 + Type + Boolean + Value + 0 + MeshUploadLogXML Comment diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 87191dd625..9157654593 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -42,14 +42,17 @@ #include "llscrolllistctrl.h" #include "llsliderctrl.h" #include "lltabcontainer.h" +#include "llcheckboxctrl.h" 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 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_"; static const std::string POSER_ADVANCEDWINDOWSTATE_SAVE_KEY = "FSPoserAdvancedWindowState"; +static const std::string POSER_ALSOSAVEBVHFILE_SAVE_KEY = "FSPoserSaveBvhFileAlso"; static const std::string POSER_AVATAR_PANEL_JOINTSPARENT = "joints_parent_panel"; static const std::string POSER_AVATAR_PANEL_TRACKBALL = "trackball_panel"; @@ -86,6 +89,8 @@ static const std::string POSER_AVATAR_ADV_SLIDER_SCALEX_NAME = "Advanced_Scale_X static const std::string POSER_AVATAR_ADV_SLIDER_SCALEY_NAME = "Advanced_Scale_Y"; static const std::string POSER_AVATAR_ADV_SLIDER_SCALEZ_NAME = "Advanced_Scale_Z"; static const std::string POSER_AVATAR_ADV_BUTTON_NAME = "start_stop_posing_button"; +static const std::string POSER_AVATAR_ADVANCED_SAVEOPTIONSPANEL_NAME = "save_file_options"; +static const std::string POSER_AVATAR_ADVANCED_SAVEBVHCHECKBOX_NAME = "also_save_bvh_checkbox"; static const std::string POSER_AVATAR_SCROLLLIST_AVATARSELECTION = "avatarSelection_scroll"; static const std::string POSER_AVATAR_STARTSTOP_POSING_BUTTON_NAME = "start_stop_posing_button"; @@ -221,6 +226,14 @@ bool FSFloaterPoser::postBuild() advancedButton->setValue(true); } + bool saveBvhCheckboxState = gSavedSettings.getBOOL(POSER_ALSOSAVEBVHFILE_SAVE_KEY); + if (saveBvhCheckboxState) + { + LLCheckBoxCtrl* saveBvhCheckbox = getChild(POSER_AVATAR_ADVANCED_SAVEBVHCHECKBOX_NAME); + if (saveBvhCheckbox) + saveBvhCheckbox->set(true); + } + LLLineEditor *poseSaveName = getChild(POSER_AVATAR_LINEEDIT_FILESAVENAME); if (poseSaveName) poseSaveName->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); @@ -246,6 +259,10 @@ void FSFloaterPoser::onClose(bool app_quitting) LLButton *advancedButton = getChild(POSER_AVATAR_ADVANCED_TOGGLEBUTTON_NAME); if (advancedButton) gSavedSettings.setBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY, advancedButton->getValue().asBoolean()); + + LLCheckBoxCtrl* saveBvhCheckbox = getChild(POSER_AVATAR_ADVANCED_SAVEBVHCHECKBOX_NAME); + if (saveBvhCheckbox) + gSavedSettings.setBOOL(POSER_ALSOSAVEBVHFILE_SAVE_KEY, saveBvhCheckbox->getValue()); } void FSFloaterPoser::refreshPosesScroll() @@ -344,9 +361,59 @@ void FSFloaterPoser::onClickPoseSave() refreshPosesScroll(); setUiSelectedAvatarSaveFileName(filename); // TODO: provide feedback for save + + LLCheckBoxCtrl* saveBvhCheckbox = getChild(POSER_AVATAR_ADVANCED_SAVEBVHCHECKBOX_NAME); + if (!saveBvhCheckbox) + return; + + bool alsoSaveAsBvh = saveBvhCheckbox->getValue().asBoolean(); + if (alsoSaveAsBvh) + savePoseToBvh(avatar, filename); } } +bool FSFloaterPoser::savePoseToBvh(LLVOAvatar* avatar, std::string poseFileName) +{ + if (poseFileName.empty()) + return false; + + if (!_poserAnimator.isPosingAvatar(avatar)) + return false; + + bool writeSuccess = false; + + try + { + std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); + if (!gDirUtilp->fileExists(pathname)) + { + LL_WARNS("Poser") << "Couldn't find folder: " << pathname << " - creating one." << LL_ENDL; + LLFile::mkdir(pathname); + } + + std::string fullSavePath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_EXTERNAL_FORMAT_FILE_EXT); + + llofstream file; + file.open(fullSavePath.c_str()); + if (!file.is_open()) + { + LL_WARNS("Poser") << "Unable to save pose!" << LL_ENDL; + return false; + } + + writeSuccess = _poserAnimator.writePoseAsBvh(&file, avatar); + + file.close(); + } + catch (...) + { + return false; + } + + return true; +} + bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, std::string poseFileName) { if (poseFileName.empty()) @@ -360,7 +427,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, std::string poseFileName) std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); if (!gDirUtilp->fileExists(pathname)) { - LL_WARNS("Posing") << "Couldn't find folder: " << pathname << " - creating one." << LL_ENDL; + LL_WARNS("Poser") << "Couldn't find folder: " << pathname << " - creating one." << LL_ENDL; LLFile::mkdir(pathname); } @@ -393,7 +460,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, std::string poseFileName) file.open(fullSavePath.c_str()); if (!file.is_open()) { - LL_WARNS("GroupMute") << "Unable to save pose!" << LL_ENDL; + LL_WARNS("Poser") << "Unable to save pose!" << LL_ENDL; return false; } LLSDSerialize::toPrettyXML(record, file); @@ -935,6 +1002,28 @@ void FSFloaterPoser::onToggleLoadSavePanel() if (loadSavePanelExpanded) refreshPosesScroll(); + + showOrHideAdvancedSaveOptions(); +} + +void FSFloaterPoser::showOrHideAdvancedSaveOptions() +{ + LLButton* yourPosesButton = getChild(POSER_AVATAR_TOGGLEBUTTON_LOADSAVE); + if (!yourPosesButton) + return; + + LLButton* advancedButton = getChild(POSER_AVATAR_ADVANCED_TOGGLEBUTTON_NAME); + if (!advancedButton) + return; + + LLUICtrl* advSavePanel = getChild(POSER_AVATAR_ADVANCED_SAVEOPTIONSPANEL_NAME); + if (!advSavePanel) + return; + + bool loadSavePanelExpanded = yourPosesButton->getValue().asBoolean(); + bool advancedPanelExpanded = advancedButton->getValue().asBoolean(); + + advSavePanel->setVisible(loadSavePanelExpanded && advancedPanelExpanded); } void FSFloaterPoser::onToggleMirrorChange() { setRotationChangeButtons(true, false); } @@ -1238,7 +1327,7 @@ void FSFloaterPoser::onToggleAdvancedPanel() return; this->reshape(poserFloaterWidth, poserFloaterHeight); - + showOrHideAdvancedSaveOptions(); onJointSelect(); } diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index f459586c41..8e7bed06e4 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -198,11 +198,13 @@ class FSFloaterPoser : public LLFloater void onClickPoseSave(); void onPoseFileSelect(); bool savePoseToXml(LLVOAvatar* avatar, std::string posePath); + bool savePoseToBvh(LLVOAvatar* avatar, std::string posePath); void onClickBrowsePoseCache(); void onPoseMenuAction(const LLSD& param); void loadPoseFromXml(LLVOAvatar* avatar, std::string poseFileName, E_LoadPoseMethods loadMethod); void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); void setUiSelectedAvatarSaveFileName(std::string saveFileName); + void showOrHideAdvancedSaveOptions(); // UI Event Handlers: void onAvatarsRefresh(); diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 6f12b1c84e..fd7f34314c 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -800,3 +800,121 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar *avatar) return true; } +bool FSPoserAnimator::writePoseAsBvh(llofstream* fileStream, LLVOAvatar* avatar) +{ + if (!fileStream || !avatar) + return false; + + *fileStream << "HIERARCHY" << std::endl; + auto startingJoint = getPoserJointByName("mPelvis"); + writeBvhFragment(fileStream, avatar, startingJoint, 0); + *fileStream << "MOTION" << std::endl; + *fileStream << "Frames: 1" << std::endl; + *fileStream << "Frame Time: 1" << std::endl; + writeBvhMotion(fileStream, avatar, startingJoint); + *fileStream << std::endl; + + return true; +} + +bool FSPoserAnimator::writeBvhFragment(llofstream* fileStream, LLVOAvatar* avatar, const FSPoserJoint* joint, int tabStops) +{ + if (!joint) + return false; + + auto position = getJointPosition(avatar, *joint); + + switch (joint->boneType()) + { + case WHOLEAVATAR: + *fileStream << "ROOT " + joint->jointName() << std::endl; + *fileStream << "{" << std::endl; + *fileStream << getTabs(tabStops + 1) + "OFFSET " + vec3ToXYZString(position) << std::endl; + *fileStream << getTabs(tabStops + 1) + "CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation" << std::endl; + break; + + default: + *fileStream << getTabs(tabStops) + "JOINT " + joint->jointName() << std::endl; + *fileStream << getTabs(tabStops) + "{" << std::endl; + *fileStream << getTabs(tabStops + 1) + "OFFSET " + vec3ToXYZString(position) << std::endl; + *fileStream << getTabs(tabStops + 1) + "CHANNELS 3 Xrotation Yrotation Zrotation" << std::endl; + break; + } + + size_t numberOfBvhChildNodes = joint->bvhChildren().size(); + if (numberOfBvhChildNodes > 0) + { + for (size_t index = 0; index != numberOfBvhChildNodes; ++index) + { + auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); + writeBvhFragment(fileStream, avatar, nextJoint, tabStops + 1); + } + } + else + { + *fileStream << getTabs(tabStops + 1) + "End Site" << std::endl; + *fileStream << getTabs(tabStops + 1) + "{" << std::endl; + *fileStream << getTabs(tabStops + 2) + "OFFSET " + vec3ToXYZString(position) << std::endl; // I don't understand this node + *fileStream << getTabs(tabStops + 1) + "}" << std::endl; + } + + *fileStream << getTabs(tabStops) + "}" << std::endl; + return true; +} + +bool FSPoserAnimator::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar, const FSPoserJoint* joint) +{ + if (!joint) + return false; + + auto rotation = getJointRotation(avatar, *joint, SWAP_NOTHING, NEGATE_NOTHING, false); + auto position = getJointPosition(avatar, *joint); + + switch (joint->boneType()) + { + case WHOLEAVATAR: + *fileStream << vec3ToXYZString(position) + " " + rotationToXZYString(rotation); + break; + + default: + *fileStream << " " + rotationToXZYString(rotation); + break; + } + + size_t numberOfBvhChildNodes = joint->bvhChildren().size(); + for (size_t index = 0; index != numberOfBvhChildNodes; ++index) + { + auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); + writeBvhMotion(fileStream, avatar, nextJoint); + } + + return true; +} + +std::string FSPoserAnimator::vec3ToXYZString(LLVector3 val) +{ + return f32ToString(val[VX]) + " " + f32ToString(val[VY]) + " " + f32ToString(val[VZ]); +} + +std::string FSPoserAnimator::rotationToXZYString(LLVector3 val) +{ + return f32ToString(val[VY] * RAD_TO_DEG) + " " + f32ToString(val[VZ] * RAD_TO_DEG) + " " + f32ToString(val[VX] * RAD_TO_DEG); +} + +std::string FSPoserAnimator::getTabs(int numOfTabstops) +{ + std::string tabSpaces; + for (int i = 0; i < numOfTabstops; i++) + tabSpaces += "\t"; + + return tabSpaces; +} + +std::string FSPoserAnimator::f32ToString(F32 val) +{ + std::string str; + char buf[20]; + snprintf(buf, 20, "%f", val); + str = buf; + return str; +} diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 70118d27bd..9715cc0507 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -100,6 +100,7 @@ public: std::string _jointName; // expected to be a match to LLJoint.getName() for a joint implementation. std::string _mirrorJointName; E_BoneTypes _boneList; + std::vector _bvhChildren; bool _dontFlipOnMirror = false; public: /// @@ -122,6 +123,11 @@ public: /// bool dontFlipOnMirror() const { return _dontFlipOnMirror; } + /// + /// Gets the collection of child bvh joints for this. + /// + std::vector bvhChildren() const { return _bvhChildren; } + /// /// Creates a new instance of a PoserJoint. /// @@ -132,13 +138,15 @@ public: /// /// The opposite joint name, if any. Also expected to be a well-known name. /// The type of bone, often determining with which other bones the new instance would appear with. - /// The option for whether this joint should rotation-flip it counterpart when mirroring the pose of the entire body. - FSPoserJoint(std::string a, std::string b, E_BoneTypes c, bool d = false) + /// The optional array of joints, needed for BVH saving, which are the direct decendent(s) of this joint. + /// The option for whether this joint should rotation-flip it counterpart when mirroring the pose of the entire body. + FSPoserJoint(std::string a, std::string b, E_BoneTypes c, std::vector d = {}, bool e = false) { _jointName = a; _mirrorJointName = b; _boneList = c; - _dontFlipOnMirror = d; + _bvhChildren = d; + _dontFlipOnMirror = e; } }; @@ -152,25 +160,25 @@ public: /// const std::vector PoserJoints { // 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, 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, true}, {"mKneeRight", "mKneeLeft", BODY, true}, {"mAnkleRight", "mAnkleLeft", BODY, true}, + {"mPelvis", "", WHOLEAVATAR, {"mTorso", "mHipLeft", "mHipRight"}}, {"mTorso", "", BODY, {"mChest"}}, {"mChest", "", BODY, {"mNeck", "mCollarLeft", "mCollarRight", "mWingsRoot"}}, {"mNeck", "", BODY, {"mHead"}}, {"mHead", "", BODY}, + {"mCollarLeft", "mCollarRight", BODY, {"mShoulderLeft"}}, {"mShoulderLeft", "mShoulderRight", BODY, {"mElbowLeft"}}, {"mElbowLeft", "mElbowRight", BODY, {"mWristLeft"}}, {"mWristLeft", "mWristRight", BODY}, + {"mCollarRight", "mCollarLeft", BODY, {"mShoulderRight"}, true}, {"mShoulderRight", "mShoulderLeft", BODY, {"mElbowRight"}, true}, {"mElbowRight", "mElbowLeft", BODY, {"mWristRight"}, true}, {"mWristRight", "mWristLeft", BODY, {}, true}, + {"mHipLeft", "mHipRight", BODY, {"mKneeLeft"}}, {"mKneeLeft", "mKneeRight", BODY, {"mAnkleLeft"}}, {"mAnkleLeft", "mAnkleRight", BODY}, + {"mHipRight", "mHipLeft", BODY, {"mKneeRight"}, true}, {"mKneeRight", "mKneeLeft", BODY, {"mAnkleRight"}, true}, {"mAnkleRight", "mAnkleLeft", BODY, {}, true}, // face - {"mFaceForeheadLeft", "mFaceForeheadRight", FACE}, {"mFaceForeheadCenter", "", FACE}, {"mFaceForeheadRight", "mFaceForeheadLeft", FACE, true}, + {"mFaceForeheadLeft", "mFaceForeheadRight", FACE}, {"mFaceForeheadCenter", "", FACE}, {"mFaceForeheadRight", "mFaceForeheadLeft", FACE, {}, true}, {"mFaceEyebrowOuterLeft", "mFaceEyebrowOuterRight", FACE}, {"mFaceEyebrowCenterLeft", "mFaceEyebrowCenterRight", FACE}, {"mFaceEyebrowInnerLeft", "mFaceEyebrowInnerRight", FACE}, - {"mFaceEyebrowOuterRight", "mFaceEyebrowOuterLeft", FACE, true}, {"mFaceEyebrowCenterRight", "mFaceEyebrowCenterLeft", FACE, true}, {"mFaceEyebrowInnerRight", "mFaceEyebrowInnerLeft", FACE, true}, + {"mFaceEyebrowOuterRight", "mFaceEyebrowOuterLeft", FACE, {}, true}, {"mFaceEyebrowCenterRight", "mFaceEyebrowCenterLeft", FACE, {}, true}, {"mFaceEyebrowInnerRight", "mFaceEyebrowInnerLeft", FACE, {}, true}, - {"mEyeLeft", "mEyeRight", FACE}, {"mEyeRight", "mEyeLeft", FACE, true}, - {"mFaceEyeLidUpperLeft", "mFaceEyeLidUpperRight", FACE}, {"mFaceEyeLidLowerLeft", "mFaceEyeLidLowerRight", FACE}, {"mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE, true}, {"mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE, true}, + {"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, true}, {"mFaceCheekLowerRight", "mFaceCheekLowerLeft", FACE, true}, - {"mFaceLipUpperLeft", "mFaceLipUpperRight", FACE}, {"mFaceLipUpperCenter", "", FACE}, {"mFaceLipUpperRight", "mFaceLipUpperLeft", FACE, true}, - {"mFaceLipCornerLeft", "mFaceLipCornerRight", FACE}, {"mFaceLipCornerRight", "mFaceLipCornerLeft", FACE, true}, - {"mFaceTongueBase", "", FACE}, {"mFaceTongueTip", "", FACE, true}, - {"mFaceLipLowerLeft", "mFaceLipLowerRight", FACE}, {"mFaceLipLowerCenter", "", FACE}, {"mFaceLipLowerRight", "mFaceLipLowerLeft", FACE, true}, + {"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}, + {"mFaceTongueBase", "", FACE}, {"mFaceTongueTip", "", FACE, {}, true}, + {"mFaceLipLowerLeft", "mFaceLipLowerRight", FACE}, {"mFaceLipLowerCenter", "", FACE}, {"mFaceLipLowerRight", "mFaceLipLowerLeft", FACE, {}, true}, {"mFaceJaw", "", FACE}, //left hand @@ -181,25 +189,25 @@ public: {"mHandPinky1Left", "mHandPinky1Right", HANDS}, {"mHandPinky2Left", "mHandPinky2Right", HANDS}, {"mHandPinky3Left", "mHandPinky3Right", HANDS}, // right hand - {"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}, + {"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, true}, {"mHindLimb2Right", "mHindLimb2Left", MISC, true}, {"mHindLimb3Right", "mHindLimb3Left", MISC, true}, {"mHindLimb4Right", "mHindLimb4Left", MISC, true}, + {"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, true}, {"mWing2Right", "mWing2Left", MISC, true}, {"mWing3Right", "mWing3Left", MISC, true}, {"mWing4Right", "mWing4Left", MISC, true}, {"mWing4FanRight", "mWing4FanLeft", MISC, true}, + {"mWing1Right", "mWing1Left", MISC, {}, true}, {"mWing2Right", "mWing2Left", MISC, {}, true}, {"mWing3Right", "mWing3Left", MISC, {}, true}, {"mWing4Right", "mWing4Left", MISC, {}, true}, {"mWing4FanRight", "mWing4FanLeft", MISC, {}, true}, // Collision Volumes - {"LEFT_PEC", "RIGHT_PEC", COL_VOLUMES}, {"RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, true}, {"BELLY", "", COL_VOLUMES}, {"BUTT", "", COL_VOLUMES}, + {"LEFT_PEC", "RIGHT_PEC", COL_VOLUMES}, {"RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, {}, true}, {"BELLY", "", COL_VOLUMES}, {"BUTT", "", COL_VOLUMES}, }; public: @@ -390,6 +398,14 @@ public: /// The avatar whose pose should flip left-right. void flipEntirePose(LLVOAvatar *avatar); + /// + /// Determines whether the supplied PoserJoint for the supplied avatar is being posed. + /// + /// The avatar having the joint to which we refer. + /// The joint being queried for. + /// True if this is joint is being posed for the supplied avatar, otherwise false. + bool writePoseAsBvh(llofstream *fileStream, LLVOAvatar* avatar); + private: /// /// Translates a rotation vector from the UI to a Quaternion for the bone. @@ -431,7 +447,16 @@ public: /// /// The avatar to test if it is safe to animate. /// True if the avatar is safe to manipulate, otherwise false. - bool isAvatarSafeToUse(LLVOAvatar *avatar); + bool isAvatarSafeToUse(LLVOAvatar* avatar); + + bool writeBvhFragment(llofstream* fileStream, LLVOAvatar* avatar, const FSPoserJoint* joint, int tabStops); + + bool writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar, const FSPoserJoint* joint); + + std::string static f32ToString(F32 val); + std::string static getTabs(int numOfTabstops); + std::string static rotationToXZYString(LLVector3 val); + std::string static vec3ToXYZString(LLVector3 val); /// /// Maps the avatar's ID to the animation registered to them. diff --git a/indra/newview/skins/default/xui/en/floater_poser.xml b/indra/newview/skins/default/xui/en/floater_poser.xml index b37a4fa577..c879c9f435 100644 --- a/indra/newview/skins/default/xui/en/floater_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_poser.xml @@ -1115,7 +1115,7 @@ width="565"> visible="true" auto_resize="false" name="advanced_controls_layout" - width="607"> + width="600"> mouse_opaque="false" visible="false" name="advanced_parent_panel" - width="607"> + width="555"> tab_group="1" tab_position="top" top_pad="0" - width="607"> + width="555"> title="Body Part Position" name="position_panel" top="0" - width="607"> + width="555"> min_val="-0.5" name="Advanced_Position_X" top_pad="6" - width="520" > + width="530" > @@ -1184,7 +1184,7 @@ width="565"> min_val="-0.5" name="Advanced_Position_Y" top_pad="1" - width="520" > + width="530" > @@ -1204,7 +1204,7 @@ width="565"> min_val="-0.5" name="Advanced_Position_Z" top_pad="1" - width="520" > + width="530" > @@ -1283,7 +1283,7 @@ width="565"> min_val="0" name="Advanced_Scale_X" top_pad="6" - width="520" > + width="530" > @@ -1303,7 +1303,7 @@ width="565"> min_val="0" name="Advanced_Scale_Y" top_pad="1" - width="520" > + width="530" > @@ -1323,7 +1323,7 @@ width="565"> min_val="0" name="Advanced_Scale_Z" top_pad="1" - width="520" > + width="530" > @@ -1379,6 +1379,27 @@ width="565"> + + +