diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1a47d038d5..2bd9e8a094 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8100,17 +8100,6 @@ Value 0 - FSPoserResetBaseRotationOnEdit - - Comment - Whether to reset the base-rotation of a joint to zero when a user edits it. - Persist - 1 - Type - Boolean - Value - 0 - FSPoserOnSaveConfirmOverwrite Comment diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 1fd1bdbd32..bcfd183e8b 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -60,7 +60,6 @@ constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_ro constexpr char BVH_JOINT_TRANSFORM_STRING_PREFIX[] = "bvh_joint_transform_"; constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity"; 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"; constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work"; @@ -212,8 +211,7 @@ bool FSFloaterPoser::postBuild() mCollisionVolumesPnl = getChild("collision_volumes_panel"); mAlsoSaveBvhCbx = getChild("also_save_bvh_checkbox"); - mResetBaseRotCbx = getChild("reset_base_rotation_on_edit_checkbox"); - mResetBaseRotCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSetBaseRotZero(); }); + mAlsoSaveBvhCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSavingToBvh(); }); mTrackpadSensitivitySpnr = getChild("trackpad_sensitivity_spinner"); mYawSpnr = getChild("limb_yaw_spinner"); @@ -366,10 +364,10 @@ void FSFloaterPoser::onPoseFileSelect() mPoseSaveNameEditor->setText(name); bool isDeltaSave = !poseFileStartsFromTeePose(name); - if (isDeltaSave && hasString("LoadDiffLabel")) - mLoadPosesBtn->setLabel(getString("LoadDiffLabel")); - else if (hasString("LoadPoseLabel")) - mLoadPosesBtn->setLabel(getString("LoadPoseLabel")); + if (isDeltaSave) + mLoadPosesBtn->setLabel(tryGetString("LoadDiffLabel")); + else + mLoadPosesBtn->setLabel(tryGetString("LoadPoseLabel")); } void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename) @@ -386,24 +384,21 @@ void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename) if (getSavingToBvh()) savePoseToBvh(avatar, filename); - if (hasString(ICON_SAVE_OK)) - mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign()); - + mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign()); setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar)); } else { - if (hasString(ICON_SAVE_FAILED)) - mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign()); - } + mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign()); + } } void FSFloaterPoser::onClickPoseSave() { std::string filename = mPoseSaveNameEditor->getValue().asString(); - if (filename.empty() && hasString(ICON_SAVE_FAILED)) + if (filename.empty()) { - mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign()); + mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign()); return; } @@ -444,8 +439,7 @@ void FSFloaterPoser::onClickPoseSave() void FSFloaterPoser::onMouseLeaveSavePoseBtn() { - if (hasString("icon_save_button")) - mSavePosesBtn->setImageOverlay(getString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign()); + mSavePosesBtn->setImageOverlay(tryGetString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign()); LLVOAvatar* avatar = getUiSelectedAvatar(); if (!avatar) @@ -691,7 +685,8 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName) if (!poserJoint) return; - mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, getUiSelectedBoneDeflectionStyle()); + bool savingToExternal = getSavingToBvh(); + mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, savingToExternal, getUiSelectedBoneDeflectionStyle()); refreshRotationSlidersAndSpinners(); refreshPositionSlidersAndSpinners(); @@ -723,6 +718,9 @@ void FSFloaterPoser::onClickSymmetrize(const S32 ID) refreshRotationSlidersAndSpinners(); enableOrDisableRedoAndUndoButton(); refreshTrackpadCursor(); + + if (getSavingToBvh()) + refreshTextHighlightingOnJointScrollLists(); } void FSFloaterPoser::onCommitSpinner(const LLUICtrl* spinner, const S32 id) @@ -964,6 +962,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand) mPoserAnimator.loadJointRotation(avatar, poserJoint, true, vec3); } } + + addBoldToScrollList(mHandJointsScrollList, avatar); } catch ( const std::exception& e ) { @@ -1310,8 +1310,8 @@ LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHead return NULL; std::string headerValue = ""; - if (isHeaderRow && hasString("icon_category")) - headerValue = getString("icon_category"); + if (isHeaderRow) + headerValue = tryGetString("icon_category"); std::string jointValue = jointName; std::string parameterName = (isHeaderRow ? XML_LIST_HEADER_STRING_PREFIX : XML_LIST_TITLE_STRING_PREFIX) + jointName; @@ -1404,6 +1404,8 @@ void FSFloaterPoser::onUndoLastChange() refreshPositionSlidersAndSpinners(); refreshScaleSlidersAndSpinners(); refreshTrackpadCursor(); + if (getSavingToBvh()) + refreshTextHighlightingOnJointScrollLists(); } void FSFloaterPoser::onSetAvatarToTpose() @@ -1447,6 +1449,8 @@ void FSFloaterPoser::onResetJoint(const LLSD data) refreshScaleSlidersAndSpinners(); refreshTrackpadCursor(); enableOrDisableRedoAndUndoButton(); + if (getSavingToBvh()) + refreshTextHighlightingOnJointScrollLists(); } void FSFloaterPoser::onRedoLastChange() @@ -1474,6 +1478,8 @@ void FSFloaterPoser::onRedoLastChange() refreshTrackpadCursor(); refreshScaleSlidersAndSpinners(); refreshPositionSlidersAndSpinners(); + if (getSavingToBvh()) + refreshTextHighlightingOnJointScrollLists(); } void FSFloaterPoser::enableOrDisableRedoAndUndoButton() @@ -1967,7 +1973,7 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con return; auto selectedJoints = getUiSelectedPoserJoints(); - bool savingToExternal = getWhetherToResetBaseRotationOnEdit(); + bool savingToExternal = getSavingToBvh(); E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); for (auto item : selectedJoints) @@ -2081,7 +2087,6 @@ void FSFloaterPoser::onJointTabSelect() refreshTrackpadCursor(); enableOrDisableRedoAndUndoButton(); refreshScaleSlidersAndSpinners(); - onClickSetBaseRotZero(); } E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const @@ -2348,8 +2353,7 @@ void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists() void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff) { - if (hasString("SavePoseLabel") && hasString("SaveDiffLabel")) - setAsSaveDiff ? mSavePosesBtn->setLabel(getString("SaveDiffLabel")) : mSavePosesBtn->setLabel(getString("SavePoseLabel")); + setAsSaveDiff ? mSavePosesBtn->setLabel(tryGetString("SaveDiffLabel")) : mSavePosesBtn->setLabel(tryGetString("SavePoseLabel")); } void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar) @@ -2361,32 +2365,56 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava return; std::string iconValue = ""; - bool considerExternalFormatSaving = getWhetherToResetBaseRotationOnEdit(); - - if (considerExternalFormatSaving && hasString("icon_rotation_is_own_work")) - iconValue = getString("icon_rotation_is_own_work"); + bool considerExternalFormatSaving = getSavingToBvh(); for (auto listItem : list->getAllData()) { - FSPoserAnimator::FSPoserJoint *userData = static_cast(listItem->getUserdata()); - if (!userData) + FSPoserAnimator::FSPoserJoint *poserJoint = static_cast(listItem->getUserdata()); + if (!poserJoint) continue; if (considerExternalFormatSaving) - { - if (mPoserAnimator.baseRotationIsZero(avatar, *userData)) - ((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue(iconValue); - else - ((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue(""); - } + ((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint)); + else + ((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(""); - if (mPoserAnimator.isPosingAvatarJoint(avatar, *userData)) + if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint)) ((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD); else ((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL); } } +std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint) +{ + if (!avatar) + return ""; + + if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint)) + return tryGetString("icon_rotation_is_world_locked"); + + if (joint.boneType() == COL_VOLUMES) + return tryGetString("icon_rotation_does_not_export"); + + if (mPoserAnimator.userSetBaseRotationToZero(avatar, joint)) + { + if (mPoserAnimator.exportRotationWillLockJoint(avatar, joint)) + return tryGetString("icon_rotation_bvh_locked_edited"); + else + return tryGetString("icon_rotation_bvh_locked_unedited"); + } + else + return tryGetString("icon_rotation_bvh_unlocked"); +} + +std::string FSFloaterPoser::tryGetString(std::string name) +{ + if (name.empty()) + return ""; + + return hasString(name) ? getString(name) : ""; +} + bool FSFloaterPoser::savePoseToBvh(LLVOAvatar* avatar, const std::string& poseFileName) { if (poseFileName.empty()) @@ -2552,7 +2580,7 @@ void FSFloaterPoser::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar, if (!joint) return; - auto rotation = mPoserAnimator.getJointRotation(avatar, *joint, SWAP_NOTHING, NEGATE_NOTHING); + auto rotation = mPoserAnimator.getJointExportRotation(avatar, *joint); auto position = mPoserAnimator.getJointPosition(avatar, *joint); switch (joint->boneType()) @@ -2644,12 +2672,9 @@ 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() { - return getWhetherToResetBaseRotationOnEdit() && gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); + return gSavedSettings.getBOOL(POSER_SAVEEXTERNALFORMAT_SAVE_KEY); } +void FSFloaterPoser::onClickSavingToBvh() { refreshTextHighlightingOnJointScrollLists(); } diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 3603ca659b..78d46f0b90 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -264,7 +264,7 @@ public: void onClickLoadLeftHandPose(); void onClickLoadRightHandPose(); void onClickLoadHandPose(bool isRightHand); - void onClickSetBaseRotZero(); + void onClickSavingToBvh(); void onCommitSpinner(const LLUICtrl* spinner, const S32 ID); void onCommitSlider(const LLUICtrl* slider, const S32 id); void onClickSymmetrize(const S32 ID); @@ -351,12 +351,19 @@ public: void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar); /// - /// Gets whether the user wishes to reset the base-rotation to zero when they start editing a joint. + /// Gets a string for a joint on a scroll-list. /// - /// - /// If a joint has a base-rotation of zero, the rotation then appears to be the user's work and qualifies to save to a re-importable format. - /// - bool getWhetherToResetBaseRotationOnEdit(); + /// The avatar owning the supplied joint. + /// The joint to query. + /// A string naming an icon to present with the joint. + std::string getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint); + + /// + /// Tries to get the named string from the XUI. + /// + /// The name of the string. + /// The named string, if it exists, otherwise an empty string. + std::string tryGetString(std::string name); /// /// Gets the name of an item from the supplied object ID. @@ -503,7 +510,6 @@ public: LLPanel* mCollisionVolumesPnl{ nullptr }; LLPanel* mPosesLoadSavePnl{ nullptr }; - LLCheckBoxCtrl* mResetBaseRotCbx{ nullptr }; LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr }; LLUICtrl* mTrackpadSensitivitySpnr{ nullptr }; diff --git a/indra/newview/fsjointpose.cpp b/indra/newview/fsjointpose.cpp index 608e737aba..c0c6b365b5 100644 --- a/indra/newview/fsjointpose.cpp +++ b/indra/newview/fsjointpose.cpp @@ -57,9 +57,13 @@ void FSJointPose::setPublicPosition(const LLVector3& pos) mCurrentState.mPosition.set(pos); } -void FSJointPose::setPublicRotation(const LLQuaternion& rot) +void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot) { addStateToUndo(FSJointState(mCurrentState)); + + if (zeroBase) + zeroBaseRotation(); + mCurrentState.mRotation.set(rot); } @@ -79,6 +83,12 @@ void FSJointPose::redoLastChange() mCurrentState = redoLastStateChange(FSJointState(mCurrentState)); } +void FSJointPose::resetJoint() +{ + addStateToUndo(FSJointState(mCurrentState)); + mCurrentState.resetJoint(); +} + void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo) { auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState; @@ -125,7 +135,7 @@ FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToS mUndoneJointStatesIndex -= 1; mUndoneJointStatesIndex = llclamp(mUndoneJointStatesIndex, 0, mLastSetJointStates.size() - 1); - auto result = mLastSetJointStates.at(mUndoneJointStatesIndex); + FSJointState result = mLastSetJointStates.at(mUndoneJointStatesIndex); if (mUndoneJointStatesIndex == 0) mLastSetJointStates.pop_front(); @@ -145,14 +155,14 @@ void FSJointPose::recaptureJoint() mCurrentState = FSJointState(joint); } -void FSJointPose::recaptureJointAsDelta() +void FSJointPose::recaptureJointAsDelta(bool zeroBase) { LLJoint* joint = mJointState->getJoint(); if (!joint) return; addStateToUndo(FSJointState(mCurrentState)); - mCurrentState.updateFromJoint(joint); + mCurrentState.updateFromJoint(joint, zeroBase); } void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint) @@ -203,9 +213,6 @@ void FSJointPose::zeroBaseRotation() if (mIsCollisionVolume) return; - if (!isBaseRotationZero()) - purgeUndoQueue(); - mCurrentState.zeroBaseRotation(); } @@ -219,10 +226,21 @@ bool FSJointPose::isBaseRotationZero() const void FSJointPose::purgeUndoQueue() { + if (mIsCollisionVolume) + return; + mUndoneJointStatesIndex = 0; mLastSetJointStates.clear(); } +bool FSJointPose::userHaseSetBaseRotationToZero() const +{ + if (mIsCollisionVolume) + return false; + + return mCurrentState.userSetBaseRotationToZero(); +} + bool FSJointPose::canPerformUndo() const { switch (mLastSetJointStates.size()) diff --git a/indra/newview/fsjointpose.h b/indra/newview/fsjointpose.h index 05c96475ff..4f4eb81205 100644 --- a/indra/newview/fsjointpose.h +++ b/indra/newview/fsjointpose.h @@ -77,6 +77,11 @@ class FSJointPose /// void redoLastChange(); + /// + /// Resets the joint to its conditions when posing started. + /// + void resetJoint(); + /// /// Gets the 'public' rotation of the joint. /// @@ -85,12 +90,14 @@ class FSJointPose /// /// Sets the 'public' rotation of the joint. /// + /// Whether to zero the base rotation on setting the supplied rotation. + /// The change in rotation to apply. /// /// 'Public rotation' is the amount of rotation the user has added to the initial state. /// Public rotation is what a user may save to an external format (such as BVH). /// This distinguishes 'private' rotation, which is the state inherited from something like a pose in-world. /// - void setPublicRotation(const LLQuaternion& rot); + void setPublicRotation(bool zeroBase, const LLQuaternion& rot); /// /// Reflects the base and delta rotation of the represented joint left-right. @@ -153,13 +160,20 @@ class FSJointPose /// /// Recalculates the delta reltive to the base for a new rotation. /// - void recaptureJointAsDelta(); + /// Whether to zero the base rotation on setting the supplied rotation. + void recaptureJointAsDelta(bool zeroBase); /// /// Clears the undo/redo deque. /// void purgeUndoQueue(); + /// + /// Gets whether the user has specified the base rotation of a joint to be zero. + /// + /// True if the user performed some action to specify zero rotation as the base, otherwise false. + bool userHaseSetBaseRotationToZero() const; + /// /// Reverts the position/rotation/scale to their values when the animation begun. /// This treatment is required for certain joints, particularly Collision Volumes and those bones not commonly animated by an AO. @@ -180,6 +194,7 @@ class FSJointPose public: FSJointState(LLJoint* joint) { + mStartingRotation.set(joint->getRotation()); mBaseRotation.set(joint->getRotation()); mBasePosition.set(joint->getPosition()); mBaseScale.set(joint->getScale()); @@ -209,11 +224,27 @@ class FSJointPose { mBaseRotation.set(otherState.mBaseRotation); mRotation.set(otherState.mRotation); + mUserSpecifiedBaseZero = otherState.userSetBaseRotationToZero(); } + bool userSetBaseRotationToZero() const { return mUserSpecifiedBaseZero; } + bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; } - void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; } + void resetJoint() + { + mUserSpecifiedBaseZero = false; + mBaseRotation.set(mStartingRotation); + mRotation.set(LLQuaternion::DEFAULT); + mPosition.setZero(); + mScale.setZero(); + } + + void zeroBaseRotation() + { + mBaseRotation = LLQuaternion::DEFAULT; + mUserSpecifiedBaseZero = true; + } void revertJointToBase(LLJoint* joint) const { @@ -225,7 +256,7 @@ class FSJointPose joint->setScale(mBaseScale); } - void updateFromJoint(LLJoint* joint) + void updateFromJoint(LLJoint* joint, bool zeroBase) { if (!joint) return; @@ -233,6 +264,10 @@ class FSJointPose LLQuaternion invRot = mBaseRotation; invRot.conjugate(); mRotation = joint->getRotation() * invRot; + + if (zeroBase) + zeroBaseRotation(); + mPosition.set(joint->getPosition() - mBasePosition); mScale.set(joint->getScale() - mBaseScale); } @@ -240,6 +275,7 @@ class FSJointPose private: FSJointState(FSJointState* state) { + mStartingRotation.set(state->mStartingRotation); mBaseRotation.set(state->mBaseRotation); mBasePosition.set(state->mBasePosition); mBaseScale.set(state->mBaseScale); @@ -247,6 +283,7 @@ class FSJointPose mRotation.set(state->mRotation); mPosition.set(state->mPosition); mScale.set(state->mScale); + mUserSpecifiedBaseZero = state->userSetBaseRotationToZero(); } public: @@ -255,9 +292,11 @@ class FSJointPose LLVector3 mScale; private: + LLQuaternion mStartingRotation; LLQuaternion mBaseRotation; LLVector3 mBasePosition; LLVector3 mBaseScale; + bool mUserSpecifiedBaseZero = false; }; private: diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 62d9030c85..81570866f8 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -122,9 +122,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, if (!jointPose) return; - jointPose->setPublicRotation(LLQuaternion()); - jointPose->setPublicPosition(LLVector3()); - jointPose->setPublicScale(LLVector3()); + jointPose->resetJoint(); if (style == NONE || style == DELTAMODE) return; @@ -133,9 +131,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, if (!oppositeJointPose) return; - oppositeJointPose->setPublicRotation(LLQuaternion()); - oppositeJointPose->setPublicPosition(LLVector3()); - oppositeJointPose->setPublicScale(LLVector3()); + oppositeJointPose->resetJoint(); } bool FSPoserAnimator::canRedoOrUndoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, bool canUndo) @@ -265,7 +261,7 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j } } -bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const +bool FSPoserAnimator::getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const { if (!isAvatarSafeToUse(avatar)) return false; @@ -278,7 +274,53 @@ bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& if (!jointPose) return false; - return jointPose->isBaseRotationZero(); + // TODO: FIRE-35769 + + return false; +} + +bool FSPoserAnimator::exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const +{ + const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; // this is a guestimate: see BVH loader + + if (!isAvatarSafeToUse(avatar)) + return false; + + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return false; + + FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + if (!jointPose) + return false; + + if (!jointPose->userHaseSetBaseRotationToZero()) + return false; + + F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f); + LLQuaternion rotToExport = jointPose->getPublicRotation(); + + F32 x_delta = dist_vec(LLVector3::x_axis * LLQuaternion::DEFAULT, LLVector3::x_axis * rotToExport); // when exporting multiple frames this will need to compare frames. + F32 y_delta = dist_vec(LLVector3::y_axis * LLQuaternion::DEFAULT, LLVector3::y_axis * rotToExport); + F32 rot_test = x_delta + y_delta; + + return rot_test > rot_threshold; +} + +bool FSPoserAnimator::userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const +{ + if (!isAvatarSafeToUse(avatar)) + return false; + + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return false; + + FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + if (!jointPose) + return false; + + return jointPose->userHaseSetBaseRotationToZero(); } bool FSPoserAnimator::allBaseRotationsAreZero(LLVOAvatar* avatar) const @@ -326,7 +368,8 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi setPosingAvatarJoint(avatar, joint, true); } -void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style) +void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, + E_BoneDeflectionStyles style) { if (!isAvatarSafeToUse(avatar)) return; @@ -339,7 +382,7 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi if (!jointPose) return; - jointPose->recaptureJointAsDelta(); + jointPose->recaptureJointAsDelta(resetBaseRotationToZero); if (style == NONE || style == DELTAMODE) return; @@ -365,6 +408,35 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi } } +LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint) const +{ + auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING); + if (exportRotationWillLockJoint(avatar, joint)) + return rotation; + + LLVector3 vec3; + if (!isAvatarSafeToUse(avatar)) + return vec3; + + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return vec3; + + FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + if (!jointPose) + return vec3; + + if (!jointPose->userHaseSetBaseRotationToZero()) + return vec3; + + if (joint.boneType() == WHOLEAVATAR) + return LLVector3(DEG_TO_RAD * 0.295f, 0.f, 0.f); + + F32 minimumRotation = DEG_TO_RAD * 0.65f / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f); + + return LLVector3(minimumRotation, 0.f, 0.f); +} + LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const { LLVector3 vec3; @@ -399,9 +471,6 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j if (!jointPose) return; - if (resetBaseRotationToZero) - jointPose->zeroBaseRotation(); - LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation); LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation); switch (deflectionStyle) @@ -409,27 +478,27 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j case SYMPATHETIC: case MIRROR: if (rotationStyle == DELTAIC_ROT) - jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation()); + jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); else - jointPose->setPublicRotation(absRot); + jointPose->setPublicRotation(resetBaseRotationToZero, absRot); break; case SYMPATHETIC_DELTA: case MIRROR_DELTA: - jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation()); + jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); break; case DELTAMODE: - jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation()); + jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); return; case NONE: default: if (rotationStyle == DELTAIC_ROT) - jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation()); + jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); else - jointPose->setPublicRotation(absRot); + jointPose->setPublicRotation(resetBaseRotationToZero, absRot); return; } @@ -446,7 +515,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j break; case SYMPATHETIC_DELTA: - oppositeJointPose->setPublicRotation(deltaRot * oppositeJointPose->getPublicRotation()); + oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation()); break; case MIRROR: @@ -455,7 +524,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j case MIRROR_DELTA: inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]); - oppositeJointPose->setPublicRotation(inv_quat * oppositeJointPose->getPublicRotation()); + oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation()); break; default: @@ -757,11 +826,10 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* if (!jointPose) return; - if (setBaseToZero) - jointPose->zeroBaseRotation(); + jointPose->purgeUndoQueue(); LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation); - jointPose->setPublicRotation(rot); + jointPose->setPublicRotation(setBaseToZero, rot); } void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position) @@ -802,7 +870,7 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi jointPose->setPublicScale(scale); } -const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) +const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const { for (size_t index = 0; index != PoserJoints.size(); ++index) { @@ -899,3 +967,23 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const return true; } + +int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) const +{ + size_t numberOfBvhChildNodes = joint->bvhChildren().size(); + if (numberOfBvhChildNodes < 1) + return depth; + + depth++; + + for (size_t index = 0; index != numberOfBvhChildNodes; ++index) + { + auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); + if (!nextJoint) + continue; + + depth = llmax(depth, getChildJointDepth(nextJoint, depth)); + } + + return depth; +} diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 8e34b9ba59..c7995cf5ea 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -392,7 +392,7 @@ public: /// /// The name of the joint to match. /// The matching joint if found, otherwise nullptr - const FSPoserJoint* getPoserJointByName(const std::string& jointName); + const FSPoserJoint* getPoserJointByName(const std::string& jointName) const; /// /// Tries to start posing the supplied avatar. @@ -492,6 +492,18 @@ public: /// The rotation of the requested joint, if determinable, otherwise a default vector. LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const; + /// + /// Gets the rotation of a joint for the supplied avatar for export. + /// + /// The avatar whose joint is being queried. + /// The joint to determine the rotation for. + /// The rotation of the requested joint for export. + /// + /// The BVH export format requires some minimal amount of rotation so it animates the joint on upload. + /// The WHOLEAVATAR joint (mPelvis) never exports as 'free'. + /// + LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint) const; + /// /// Sets the rotation of a joint for the supplied avatar. /// @@ -559,8 +571,9 @@ public: /// /// The avatar whose joint is to be recaptured. /// The joint to recapture. + /// Whether to set the base rotation to zero on setting the rotation. /// Any ancilliary action to be taken with the change to be made. - void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style); + void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style); /// /// Sets all of the joint rotations of the supplied avatar to zero. @@ -574,7 +587,26 @@ public: /// 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; + bool userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const; + + /// + /// Gets whether the supplied joints position will be set in an export. + /// + /// The avatar owning the supplied joint. + /// The joint to query. + /// True if the export will 'lock' the joint, otherwise false. + /// + /// BVH import leaves a joint 'free' if its rotation is less than something arbitrary. + /// + bool exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const; + + /// + /// Gets whether the supplied joint for the supplied avatar is rotationally locked to the world. + /// + /// The avatar owning the supplied joint. + /// The joint to query. + /// True if the joint is maintaining a fixed-rotation in world, otherwise false. + bool getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const; /// /// Determines if the kind of save to perform should be a 'delta' save, or a complete save. @@ -692,6 +724,14 @@ public: /// True if the avatar is safe to manipulate, otherwise false. bool isAvatarSafeToUse(LLVOAvatar* avatar) const; + /// + /// Gets the depth of descendant joints for the supplied joint. + /// + /// The joint to determine the depth for. + /// The depth of the supplied joint. + /// The number of generations of descendents the joint has, if none, then zero. + int getChildJointDepth(const FSPoserJoint* joint, int depth) const; + /// /// Maps the avatar's ID to the animation registered to them. /// Thus we start/stop the same animation, and get/set the same rotations etc. diff --git a/indra/newview/fsposingmotion.cpp b/indra/newview/fsposingmotion.cpp index 841ab3a452..900f26b195 100644 --- a/indra/newview/fsposingmotion.cpp +++ b/indra/newview/fsposingmotion.cpp @@ -259,8 +259,8 @@ void FSPosingMotion::setAllRotationsToZeroAndClearUndo() { for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) { - poserJoint_iter->zeroBaseRotation(); - poserJoint_iter->setPublicRotation(LLQuaternion::DEFAULT); + poserJoint_iter->purgeUndoQueue(); + poserJoint_iter->setPublicRotation(true, LLQuaternion::DEFAULT); } } 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 dc6646c554..96c3923afc 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,11 @@ width="430"> Inv_BodyShape Inv_Object + Script_Running + Script_NotRunning + Script_Error + Locked_Icon + Conv_toolbar_close Check_Mark Icon_Dock_Foreground Parcel_Exp_Color @@ -965,25 +970,14 @@ width="430"> tool_tip="Not stopping your pose can be helpful if you do a lot of work, and don't want to accidentally lose it." top_pad="10" width="134" /> -