diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 7851bf9d5f..41f3877777 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -307,6 +307,12 @@ void FSFloaterPoser::onPoseFileSelect() LLStringExplicit name = LLStringExplicit(poseName); mPoseSaveNameEditor->setEnabled(enableButtons); mPoseSaveNameEditor->setText(name); + + bool isDeltaSave = !poseFileStartsFromTeePose(name); + if (isDeltaSave) + mLoadPosesBtn->setLabel("Load Diff"); + else + mLoadPosesBtn->setLabel("Load Pose"); } void FSFloaterPoser::onClickPoseSave() @@ -348,13 +354,13 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi std::string fullSavePath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT); - bool fromZeroRot = mPoserAnimator.posingStartedFromZeroRotations(avatar); + bool savingDiff = !mPoserAnimator.posingStartedFromZeroRotations(avatar); LLSD record; record["version"]["value"] = (S32)4; - record["startFromTeePose"]["value"] = fromZeroRot; + record["startFromTeePose"]["value"] = !savingDiff; - LLVector3 rotation, position, scale; + LLVector3 rotation, position, scale, zeroVector; for (const FSPoserAnimator::FSPoserJoint& pj : mPoserAnimator.PoserJoints) { @@ -362,8 +368,13 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi if (!mPoserAnimator.tryGetJointSaveVectors(avatar, pj, &rotation, &position, &scale)) continue; - record[bone_name] = pj.jointName(); - record[bone_name]["enabled"] = mPoserAnimator.isPosingAvatarJoint(avatar, pj); + bool jointRotPosScaleAllZero = rotation == zeroVector && position == zeroVector && scale == zeroVector; + bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj); + if (savingDiff && (!posingThisJoint || jointRotPosScaleAllZero)) + continue; + + record[bone_name] = bone_name; + record[bone_name]["enabled"] = posingThisJoint; record[bone_name]["rotation"] = rotation.getValue(); record[bone_name]["position"] = position.getValue(); record[bone_name]["scale"] = scale.getValue(); @@ -494,17 +505,7 @@ void FSFloaterPoser::onClickRecaptureSelectedBones() if (currentlyPosing) continue; - LLVector3 newRotation = mPoserAnimator.getJointRotation(avatar, *item, getJointTranslation(item->jointName()), - getJointNegation(item->jointName()), CURRENTROTATION); - LLVector3 newPosition = mPoserAnimator.getJointPosition(avatar, *item, true); - LLVector3 newScale = mPoserAnimator.getJointScale(avatar, *item, true); - - mPoserAnimator.setPosingAvatarJoint(avatar, *item, true); - - mPoserAnimator.setJointRotation(avatar, item, newRotation, NONE, getJointTranslation(item->jointName()), - getJointNegation(item->jointName())); - mPoserAnimator.setJointPosition(avatar, item, newPosition, NONE); - mPoserAnimator.setJointScale(avatar, item, newScale, NONE); + mPoserAnimator.recaptureJoint(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName())); } refreshRotationSliders(); @@ -674,6 +675,51 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand) } +bool FSFloaterPoser::poseFileStartsFromTeePose(const std::string& poseFileName) +{ + std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); + if (!gDirUtilp->fileExists(pathname)) + return false; + + std::string fullPath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT); + + try + { + LLSD pose; + llifstream infile; + bool startFromZeroRot = false; + + infile.open(fullPath); + if (!infile.is_open()) + return false; + + S32 lineCount = LLSDSerialize::fromXML(pose, infile); + if (lineCount == LLSDParser::PARSE_FAILURE) + { + LL_WARNS("Posing") << "Failed to parse file: " << poseFileName << LL_ENDL; + return startFromZeroRot; + } + + 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 == "startFromTeePose") + startFromZeroRot = control_map["value"].asBoolean(); + } + + return startFromZeroRot; + } + catch (const std::exception& e) + { + LL_WARNS("Posing") << "Unable to load or parse the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL; + } + + return false; +} + void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod) { std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); @@ -732,7 +778,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose if (version > 3) loadPositionsAndScalesAsDeltas = true; - if (startFromZeroRot) // old save formats will always start from T-Pose, for better or worse. + if (startFromZeroRot) // legacy saves will always start from T-Pose, for better or worse. { disableRecapture(); mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar); @@ -772,7 +818,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose } } } - } + } catch ( const std::exception & e ) { LL_WARNS("Posing") << "Everything caught fire trying to load the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL; diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 198d6097ba..24b69c6298 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -202,6 +202,7 @@ class FSFloaterPoser : public LLFloater void onClickBrowsePoseCache(); void onPoseMenuAction(const LLSD& param); void loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod); + bool poseFileStartsFromTeePose(const std::string& poseFileName); void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); void setUiSelectedAvatarSaveFileName(const std::string& saveFileName); void showOrHideAdvancedSaveOptions(); diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index 4c9bb067aa..4f70b19a2f 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -440,6 +440,40 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar) posingMotion->setAllRotationsToZero(); } +void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) +{ + LLVector3 newRotation = getJointRotation(avatar, joint, translation, negation, CURRENTROTATION); + LLVector3 newPosition = getJointPosition(avatar, joint, true); + LLVector3 newScale = getJointScale(avatar, joint, true); + + setPosingAvatarJoint(avatar, joint, true); + setStartingJointRotation(avatar, &joint, newRotation, translation, negation); + + // recapture of positions and scale does not reset starting values, since this this could result in unwanted residue deformation after posing stops. + setJointPosition(avatar, &joint, newPosition, NONE); + setJointScale(avatar, &joint, newScale, NONE); +} + +void FSPoserAnimator::setStartingJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, + E_BoneAxisTranslation translation, S32 negation) +{ + if (!isAvatarSafeToUse(avatar)) + return; + if (!joint) + return; + + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName()); + if (!jointPose) + return; + + LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation); + jointPose->setJointStartRotations(rot_quat); +} + LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const { LLVector3 vec3; diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 1c830e250e..d181c847a8 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -32,7 +32,7 @@ /// /// Describes how we will cluster the joints/bones/thingos. -/// Each joint/bone/thingo should have one of these, . +/// Each joint/bone/thingo should have one of these, FSPoserAnimator.PoserJoints. /// typedef enum E_BoneTypes { @@ -446,7 +446,7 @@ public: /// The avatar whose joint is being queried. /// The joint to determine the rotation for. /// The joint to determine the rotation for. - /// The style of negation to apply to the set. + /// The style of negation to dis-apply to the get. /// The type of rotation to get from the supplied joint for the supplied avatar. /// The rotation of the requested joint, if determinable, otherwise a default vector. LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const; @@ -459,6 +459,7 @@ public: /// The rotation to set the joint to. /// Any ancilliary action to be taken with the change to be made. /// The axial translation form the supplied joint. + /// The style of negation to apply to the set. void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style, E_BoneAxisTranslation translation, S32 negation); @@ -492,6 +493,15 @@ public: /// The avatar whose pose should flip left-right. void flipEntirePose(LLVOAvatar* avatar); + /// + /// Recaptures the rotation, position and scale state of the supplied joint for the supplied avatar. + /// + /// 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); + /// /// Sets all of the joint rotations of the supplied avatar to zero. /// @@ -533,8 +543,8 @@ public: /// The rotation to load. /// /// All rotations we load are deltas to the current rotation the supplied joint has. - /// Whether the joint already has a rotation because some animation is playing (sp possibly a non-zero rotation), - /// or whether it is a rotation relative to zero, the result is always the same: just 'add' this rotation to the existing. + /// Whether the joint already has a rotation because some animation is playing, + /// or whether its rotation is zero, the result is always the same: just 'add' the supplied rotation to the existing rotation. /// void loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation); @@ -549,7 +559,7 @@ public: /// A position is saved as an absolute if the user created the pose from 'scratch' (at present the 'T-Pose'). /// Otherwise the position is saved as a delta. /// The primary purpose is aesthetic: the numbers inside of a 'delta save file' have 'zeros everywhere'. - /// A delta-save thus accurately reflects what the user changed, and not what the original creator of the modified pose specified. + /// A delta-save thus accurately reflects what the user changed, and not what the original pose is. /// 'Legacy' (pre save format version-4) poses we expect to load as absolutes. /// void loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position); @@ -612,6 +622,9 @@ public: /// True if the avatar is safe to manipulate, otherwise false. bool isAvatarSafeToUse(LLVOAvatar* avatar) const; + void setStartingJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, + E_BoneAxisTranslation translation, S32 negation); + /// /// 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 718b5d25ca..f0f37a1455 100644 --- a/indra/newview/fsposingmotion.cpp +++ b/indra/newview/fsposingmotion.cpp @@ -269,19 +269,20 @@ bool FSPosingMotion::allStartingRotationsAreZero() const void FSPosingMotion::setAllRotationsToZero() { + LLQuaternion zeroQuat; for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) { if (poserJoint_iter->isCollisionVolume()) continue; - poserJoint_iter->setRotationsToZero(); + poserJoint_iter->setJointStartRotations(zeroQuat); } } constexpr size_t MaximumUndoQueueLength = 20; /// -/// The constant time interval, in seconds, +/// The constant time interval, in seconds, specifying whether an 'undo' value should be added. /// constexpr std::chrono::duration UndoUpdateInterval = std::chrono::duration(0.3); @@ -517,7 +518,7 @@ void FSPosingMotion::FSJointPose::revertCollisionVolume() joint->setScale(mBeginningScale); } -void FSPosingMotion::FSJointPose::setRotationsToZero() { mBeginningRotation = mTargetRotation = LLQuaternion(); } +void FSPosingMotion::FSJointPose::setJointStartRotations(LLQuaternion quat) { mBeginningRotation = mTargetRotation = quat; } FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume) { diff --git a/indra/newview/fsposingmotion.h b/indra/newview/fsposingmotion.h index 52d6905947..c20f3b07c2 100644 --- a/indra/newview/fsposingmotion.h +++ b/indra/newview/fsposingmotion.h @@ -223,9 +223,9 @@ public: void revertCollisionVolume(); /// - /// Sets all rotations to zero. + /// Sets the beginning and target rotations to the supplied rotation. /// - void setRotationsToZero(); + void setJointStartRotations(LLQuaternion quat); /// /// Gets the pointer to the jointstate for the joint this represents. 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 51c909760c..c699d97c66 100644 --- a/indra/newview/skins/default/xui/en/floater_fs_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_fs_poser.xml @@ -654,7 +654,7 @@ width="403"> background_visible="false" layout="topleft" visible="true" - title="Posee" + title="Model" mouse_opaque="false" name="avatarSelection_panel" top="0" @@ -1137,7 +1137,7 @@ width="403"> name="button_RecaptureParts" left_pad="1" top_delta="0" - tool_tip="If the selected body part(s) is/are OFF, this Recaptures what those body parts are doing right now" + tool_tip="If the selected body part(s) is/are OFF, this Recaptures what those body parts are doing right now. This button disables if you start from a T-Pose" width="45" >