commit
452deed657
|
|
@ -8111,10 +8111,10 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSPoserResetBaseRotationOnEdit</key>
|
||||
<key>FSPoserPelvisUnlockedForBvhSave</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Whether to reset the base-rotation of a joint to zero when a user edits it.</string>
|
||||
<string>Whether the mPelvis joint should be position/rotationally locked when a BVH is created.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ 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 std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave";
|
||||
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
|
||||
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
|
||||
|
||||
|
|
@ -193,6 +193,8 @@ bool FSFloaterPoser::postBuild()
|
|||
mFlipJointBtn = getChild<LLButton>("FlipJoint_avatar");
|
||||
mRecaptureBtn = getChild<LLButton>("button_RecaptureParts");
|
||||
mTogglePosingBonesBtn = getChild<LLButton>("toggle_PosingSelectedBones");
|
||||
mToggleLockWorldRotBtn = getChild<LLButton>("toggle_LockWorldRotation");
|
||||
mToggleLockWorldRotBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onClickLockWorldRotBtn(); });
|
||||
|
||||
mToggleMirrorRotationBtn = getChild<LLButton>("button_toggleMirrorRotation");
|
||||
mToggleSympatheticRotationBtn = getChild<LLButton>("button_toggleSympatheticRotation");
|
||||
|
|
@ -211,9 +213,10 @@ bool FSFloaterPoser::postBuild()
|
|||
mMiscJointsPnl = getChild<LLPanel>("misc_joints_panel");
|
||||
mCollisionVolumesPnl = getChild<LLPanel>("collision_volumes_panel");
|
||||
|
||||
mUnlockPelvisInBvhSaveCbx = getChild<LLCheckBoxCtrl>("unlock_pelvis_for_bvh_save_checkbox");
|
||||
mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
|
||||
mAlsoSaveBvhCbx = getChild<LLCheckBoxCtrl>("also_save_bvh_checkbox");
|
||||
mResetBaseRotCbx = getChild<LLCheckBoxCtrl>("reset_base_rotation_on_edit_checkbox");
|
||||
mResetBaseRotCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSetBaseRotZero(); });
|
||||
mAlsoSaveBvhCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSavingToBvh(); });
|
||||
|
||||
mTrackpadSensitivitySpnr = getChild<LLUICtrl>("trackpad_sensitivity_spinner");
|
||||
mYawSpnr = getChild<LLUICtrl>("limb_yaw_spinner");
|
||||
|
|
@ -366,10 +369,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 +389,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 +444,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)
|
||||
|
|
@ -523,6 +522,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
{
|
||||
std::string bone_name = pj.jointName();
|
||||
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
|
||||
bool jointRotLocked = mPoserAnimator.getRotationIsWorldLocked(avatar, pj);
|
||||
|
||||
record[bone_name] = bone_name;
|
||||
record[bone_name]["enabled"] = posingThisJoint;
|
||||
|
|
@ -538,9 +538,10 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
continue;
|
||||
|
||||
record[bone_name]["jointBaseRotationIsZero"] = baseRotationIsZero;
|
||||
record[bone_name]["rotation"] = rotation.getValue();
|
||||
record[bone_name]["position"] = position.getValue();
|
||||
record[bone_name]["scale"] = scale.getValue();
|
||||
record[bone_name]["rotation"] = rotation.getValue();
|
||||
record[bone_name]["position"] = position.getValue();
|
||||
record[bone_name]["scale"] = scale.getValue();
|
||||
record[bone_name]["worldLocked"] = jointRotLocked;
|
||||
}
|
||||
|
||||
std::string fullSavePath =
|
||||
|
|
@ -691,7 +692,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 +725,9 @@ void FSFloaterPoser::onClickSymmetrize(const S32 ID)
|
|||
refreshRotationSlidersAndSpinners();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
refreshTrackpadCursor();
|
||||
|
||||
if (getSavingToBvh())
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onCommitSpinner(const LLUICtrl* spinner, const S32 id)
|
||||
|
|
@ -964,6 +969,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
|
|||
mPoserAnimator.loadJointRotation(avatar, poserJoint, true, vec3);
|
||||
}
|
||||
}
|
||||
|
||||
addBoldToScrollList(mHandJointsScrollList, avatar);
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
|
|
@ -1041,6 +1048,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
LLQuaternion quat;
|
||||
bool enabled;
|
||||
bool setJointBaseRotationToZero;
|
||||
bool worldLocked;
|
||||
S32 version = 0;
|
||||
bool startFromZeroRot = true;
|
||||
|
||||
|
|
@ -1116,6 +1124,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
vec3.setValue(control_map["scale"]);
|
||||
mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
|
||||
}
|
||||
|
||||
worldLocked = control_map.has("worldLocked") ? control_map["worldLocked"].asBoolean() : false;
|
||||
mPoserAnimator.setRotationIsWorldLocked(avatar, *poserJoint, worldLocked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1310,8 +1321,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 +1415,8 @@ void FSFloaterPoser::onUndoLastChange()
|
|||
refreshPositionSlidersAndSpinners();
|
||||
refreshScaleSlidersAndSpinners();
|
||||
refreshTrackpadCursor();
|
||||
if (getSavingToBvh())
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onSetAvatarToTpose()
|
||||
|
|
@ -1447,6 +1460,7 @@ void FSFloaterPoser::onResetJoint(const LLSD data)
|
|||
refreshScaleSlidersAndSpinners();
|
||||
refreshTrackpadCursor();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onRedoLastChange()
|
||||
|
|
@ -1474,6 +1488,8 @@ void FSFloaterPoser::onRedoLastChange()
|
|||
refreshTrackpadCursor();
|
||||
refreshScaleSlidersAndSpinners();
|
||||
refreshPositionSlidersAndSpinners();
|
||||
if (getSavingToBvh())
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::enableOrDisableRedoAndUndoButton()
|
||||
|
|
@ -1967,7 +1983,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 +2097,6 @@ void FSFloaterPoser::onJointTabSelect()
|
|||
refreshTrackpadCursor();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
refreshScaleSlidersAndSpinners();
|
||||
onClickSetBaseRotZero();
|
||||
}
|
||||
|
||||
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const
|
||||
|
|
@ -2348,8 +2363,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 +2375,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<FSPoserAnimator::FSPoserJoint *>(listItem->getUserdata());
|
||||
if (!userData)
|
||||
FSPoserAnimator::FSPoserJoint *poserJoint = static_cast<FSPoserAnimator::FSPoserJoint *>(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));
|
||||
|
||||
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 (!getSavingToBvh())
|
||||
return "";
|
||||
|
||||
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 +2590,9 @@ void FSFloaterPoser::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar,
|
|||
if (!joint)
|
||||
return;
|
||||
|
||||
auto rotation = mPoserAnimator.getJointRotation(avatar, *joint, SWAP_NOTHING, NEGATE_NOTHING);
|
||||
bool lockPelvisJoint = gSavedSettings.getBOOL(POSER_UNLOCKPELVISINBVH_SAVE_KEY);
|
||||
|
||||
auto rotation = mPoserAnimator.getJointExportRotation(avatar, *joint, !lockPelvisJoint);
|
||||
auto position = mPoserAnimator.getJointPosition(avatar, *joint);
|
||||
|
||||
switch (joint->boneType())
|
||||
|
|
@ -2644,12 +2684,39 @@ 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()
|
||||
{
|
||||
mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onClickLockWorldRotBtn()
|
||||
{
|
||||
auto selectedJoints = getUiSelectedPoserJoints();
|
||||
if (selectedJoints.size() < 1)
|
||||
return;
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
for (auto item : selectedJoints)
|
||||
{
|
||||
bool currentlyPosingJoint = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
|
||||
if (!currentlyPosingJoint)
|
||||
continue;
|
||||
|
||||
bool newLockState = !mPoserAnimator.getRotationIsWorldLocked(avatar, *item);
|
||||
mPoserAnimator.setRotationIsWorldLocked(avatar, *item, newLockState);
|
||||
}
|
||||
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,10 +264,11 @@ 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);
|
||||
void onClickLockWorldRotBtn();
|
||||
|
||||
// UI Refreshments
|
||||
void refreshRotationSlidersAndSpinners();
|
||||
|
|
@ -351,12 +352,19 @@ public:
|
|||
void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
bool getWhetherToResetBaseRotationOnEdit();
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <returns>A string naming an icon to present with the joint.</returns>
|
||||
std::string getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the named string from the XUI.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the string.</param>
|
||||
/// <returns>The named string, if it exists, otherwise an empty string.</returns>
|
||||
std::string tryGetString(std::string name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of an item from the supplied object ID.
|
||||
|
|
@ -483,6 +491,7 @@ public:
|
|||
LLButton* mFlipJointBtn{ nullptr };
|
||||
LLButton* mRecaptureBtn{ nullptr };
|
||||
LLButton* mTogglePosingBonesBtn{ nullptr };
|
||||
LLButton* mToggleLockWorldRotBtn{ nullptr };
|
||||
LLButton* mToggleMirrorRotationBtn{ nullptr };
|
||||
LLButton* mToggleSympatheticRotationBtn{ nullptr };
|
||||
LLButton* mToggleDeltaModeBtn{ nullptr };
|
||||
|
|
@ -503,8 +512,8 @@ public:
|
|||
LLPanel* mCollisionVolumesPnl{ nullptr };
|
||||
LLPanel* mPosesLoadSavePnl{ nullptr };
|
||||
|
||||
LLCheckBoxCtrl* mResetBaseRotCbx{ nullptr };
|
||||
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr };
|
||||
LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr };
|
||||
|
||||
LLUICtrl* mTrackpadSensitivitySpnr{ nullptr };
|
||||
LLUICtrl* mYawSpnr{ nullptr };
|
||||
|
|
|
|||
|
|
@ -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(true);
|
||||
|
||||
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()
|
||||
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
||||
{
|
||||
LLJoint* joint = mJointState->getJoint();
|
||||
if (!joint)
|
||||
return;
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.updateFromJoint(joint);
|
||||
return mCurrentState.updateFromJoint(joint, zeroBase);
|
||||
}
|
||||
|
||||
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
|
||||
|
|
@ -198,15 +208,13 @@ void FSJointPose::reflectRotation()
|
|||
mCurrentState.reflectRotation();
|
||||
}
|
||||
|
||||
void FSJointPose::zeroBaseRotation()
|
||||
void FSJointPose::zeroBaseRotation(bool lockInBvh)
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
if (!isBaseRotationZero())
|
||||
purgeUndoQueue();
|
||||
|
||||
mCurrentState.zeroBaseRotation();
|
||||
mCurrentState.mUserSpecifiedBaseZero = lockInBvh;
|
||||
}
|
||||
|
||||
bool FSJointPose::isBaseRotationZero() const
|
||||
|
|
@ -219,10 +227,37 @@ bool FSJointPose::isBaseRotationZero() const
|
|||
|
||||
void FSJointPose::purgeUndoQueue()
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mUndoneJointStatesIndex = 0;
|
||||
mLastSetJointStates.clear();
|
||||
}
|
||||
|
||||
bool FSJointPose::userHasSetBaseRotationToZero() const
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return false;
|
||||
|
||||
return mCurrentState.mUserSpecifiedBaseZero;
|
||||
}
|
||||
|
||||
bool FSJointPose::getWorldRotationLockState() const
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return false;
|
||||
|
||||
return mCurrentState.mRotationIsWorldLocked;
|
||||
}
|
||||
|
||||
void FSJointPose::setWorldRotationLockState(bool newState)
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mCurrentState.mRotationIsWorldLocked = newState;
|
||||
}
|
||||
|
||||
bool FSJointPose::canPerformUndo() const
|
||||
{
|
||||
switch (mLastSetJointStates.size())
|
||||
|
|
|
|||
|
|
@ -77,6 +77,11 @@ class FSJointPose
|
|||
/// </summary>
|
||||
void redoLastChange();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the joint to its conditions when posing started.
|
||||
/// </summary>
|
||||
void resetJoint();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 'public' rotation of the joint.
|
||||
/// </summary>
|
||||
|
|
@ -85,12 +90,14 @@ class FSJointPose
|
|||
/// <summary>
|
||||
/// Sets the 'public' rotation of the joint.
|
||||
/// </summary>
|
||||
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
|
||||
/// <param name="rot">The change in rotation to apply.</param>
|
||||
/// <remarks>
|
||||
/// '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.
|
||||
/// </remarks>
|
||||
void setPublicRotation(const LLQuaternion& rot);
|
||||
void setPublicRotation(bool zeroBase, const LLQuaternion& rot);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the base and delta rotation of the represented joint left-right.
|
||||
|
|
@ -100,7 +107,8 @@ class FSJointPose
|
|||
/// <summary>
|
||||
/// Sets the private rotation of the represented joint to zero.
|
||||
/// </summary>
|
||||
void zeroBaseRotation();
|
||||
/// <param name="lockInBvh">Whether the joint should be locked if exported to BVH.</param>
|
||||
void zeroBaseRotation(bool lockInBvh);
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the represented joint is zero.
|
||||
|
|
@ -153,13 +161,33 @@ class FSJointPose
|
|||
/// <summary>
|
||||
/// Recalculates the delta reltive to the base for a new rotation.
|
||||
/// </summary>
|
||||
void recaptureJointAsDelta();
|
||||
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
|
||||
/// <returns>The rotation of the public difference between before and after recapture.</returns>
|
||||
LLQuaternion recaptureJointAsDelta(bool zeroBase);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the undo/redo deque.
|
||||
/// </summary>
|
||||
void purgeUndoQueue();
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the user has specified the base rotation of a joint to be zero.
|
||||
/// </summary>
|
||||
/// <returns>True if the user performed some action to specify zero rotation as the base, otherwise false.</returns>
|
||||
bool userHasSetBaseRotationToZero() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the rotation of a joint has been 'locked' so that its world rotation can remain constant while parent joints change.
|
||||
/// </summary>
|
||||
/// <returns>True if the joint is rotationally locked to the world, otherwise false.</returns>
|
||||
bool getWorldRotationLockState() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the world-rotation of a joint has been 'locked' so that as its parent joints change rotation or position, this joint keeps a constant world rotation.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state for the world-rotation lock.</param>
|
||||
void setWorldRotationLockState(bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// 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,22 +208,16 @@ class FSJointPose
|
|||
public:
|
||||
FSJointState(LLJoint* joint)
|
||||
{
|
||||
mStartingRotation.set(joint->getRotation());
|
||||
mBaseRotation.set(joint->getRotation());
|
||||
mBasePosition.set(joint->getPosition());
|
||||
mBaseScale.set(joint->getScale());
|
||||
}
|
||||
|
||||
FSJointState() = default;
|
||||
LLQuaternion mDeltaRotation;
|
||||
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
|
||||
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
|
||||
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
|
||||
void updateRotation(const LLQuaternion& newRotation)
|
||||
{
|
||||
auto inv_base = mBaseRotation;
|
||||
inv_base.conjugate();
|
||||
mDeltaRotation = newRotation * inv_base;
|
||||
};
|
||||
|
||||
void reflectRotation()
|
||||
{
|
||||
|
|
@ -209,10 +231,21 @@ class FSJointPose
|
|||
{
|
||||
mBaseRotation.set(otherState.mBaseRotation);
|
||||
mRotation.set(otherState.mRotation);
|
||||
mUserSpecifiedBaseZero = otherState.mUserSpecifiedBaseZero;
|
||||
}
|
||||
|
||||
bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; }
|
||||
|
||||
void resetJoint()
|
||||
{
|
||||
mUserSpecifiedBaseZero = false;
|
||||
mRotationIsWorldLocked = false;
|
||||
mBaseRotation.set(mStartingRotation);
|
||||
mRotation.set(LLQuaternion::DEFAULT);
|
||||
mPosition.setZero();
|
||||
mScale.setZero();
|
||||
}
|
||||
|
||||
void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; }
|
||||
|
||||
void revertJointToBase(LLJoint* joint) const
|
||||
|
|
@ -225,21 +258,33 @@ class FSJointPose
|
|||
joint->setScale(mBaseScale);
|
||||
}
|
||||
|
||||
void updateFromJoint(LLJoint* joint)
|
||||
LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase)
|
||||
{
|
||||
if (!joint)
|
||||
return;
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
LLQuaternion initalPublicRot = mRotation;
|
||||
LLQuaternion invRot = mBaseRotation;
|
||||
invRot.conjugate();
|
||||
mRotation = joint->getRotation() * invRot;
|
||||
LLQuaternion newPublicRot = joint->getRotation() * invRot;
|
||||
|
||||
if (zeroBase)
|
||||
{
|
||||
mUserSpecifiedBaseZero = zeroBase;
|
||||
zeroBaseRotation();
|
||||
}
|
||||
|
||||
mRotation.set(newPublicRot);
|
||||
mPosition.set(joint->getPosition() - mBasePosition);
|
||||
mScale.set(joint->getScale() - mBaseScale);
|
||||
|
||||
return newPublicRot *= ~initalPublicRot;
|
||||
}
|
||||
|
||||
private:
|
||||
FSJointState(FSJointState* state)
|
||||
{
|
||||
mStartingRotation.set(state->mStartingRotation);
|
||||
mBaseRotation.set(state->mBaseRotation);
|
||||
mBasePosition.set(state->mBasePosition);
|
||||
mBaseScale.set(state->mBaseScale);
|
||||
|
|
@ -247,14 +292,29 @@ class FSJointPose
|
|||
mRotation.set(state->mRotation);
|
||||
mPosition.set(state->mPosition);
|
||||
mScale.set(state->mScale);
|
||||
mUserSpecifiedBaseZero = state->mUserSpecifiedBaseZero;
|
||||
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
|
||||
}
|
||||
|
||||
public:
|
||||
LLQuaternion mRotation;
|
||||
LLVector3 mPosition;
|
||||
LLVector3 mScale;
|
||||
bool mRotationIsWorldLocked = false;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the user has explicitly set the base rotation to zero.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The base-rotation, representing any 'current animation' state when posing starts, may become zero for several reasons.
|
||||
/// Loading a Pose, editing a rotation intended to save to BVH, or setting to 'T-Pose' being examples.
|
||||
/// If a user intends on creating a BVH, zero-rotation has a special meaning upon upload: the joint is free (is not animated by that BVH).
|
||||
/// This value represents the explicit intent to have that joint be 'free' in BVH (which is sometimes undesireable).
|
||||
/// </remarks>
|
||||
bool mUserSpecifiedBaseZero = false;
|
||||
|
||||
private:
|
||||
LLQuaternion mStartingRotation;
|
||||
LLQuaternion mBaseRotation;
|
||||
LLVector3 mBasePosition;
|
||||
LLVector3 mBaseScale;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
jointPose->undoLastChange();
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -104,6 +105,10 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
oppositeJointPose->undoLastChange();
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
|
||||
if (oppositePoserJoint)
|
||||
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, false);
|
||||
}
|
||||
|
||||
void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
|
||||
|
|
@ -122,9 +127,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 +136,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)
|
||||
|
|
@ -177,6 +178,7 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
jointPose->redoLastChange();
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, true);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -186,6 +188,10 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
oppositeJointPose->redoLastChange();
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
|
||||
if (oppositePoserJoint)
|
||||
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, true);
|
||||
}
|
||||
|
||||
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
|
||||
|
|
@ -265,7 +271,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 +284,67 @@ bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint&
|
|||
if (!jointPose)
|
||||
return false;
|
||||
|
||||
return jointPose->isBaseRotationZero();
|
||||
return jointPose->getWorldRotationLockState();
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->setWorldRotationLockState(newState);
|
||||
}
|
||||
|
||||
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->userHasSetBaseRotationToZero())
|
||||
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->userHasSetBaseRotationToZero();
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::allBaseRotationsAreZero(LLVOAvatar* avatar) const
|
||||
|
|
@ -307,6 +373,21 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
|
|||
return;
|
||||
|
||||
posingMotion->setAllRotationsToZeroAndClearUndo();
|
||||
|
||||
for (size_t index = 0; index != PoserJoints.size(); ++index)
|
||||
{
|
||||
auto boneType = PoserJoints[index].boneType();
|
||||
bool setBvhToLock = boneType == BODY || boneType == WHOLEAVATAR;
|
||||
if (setBvhToLock)
|
||||
continue; // setAllRotationsToZeroAndClearUndo specified this is the default behaviour
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
|
||||
if (!jointPose)
|
||||
continue;
|
||||
|
||||
posingMotion->setJointBvhLock(jointPose, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation)
|
||||
|
|
@ -326,7 +407,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 +421,9 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->recaptureJointAsDelta();
|
||||
LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -365,6 +449,35 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
|
|||
}
|
||||
}
|
||||
|
||||
LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) 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->userHasSetBaseRotationToZero())
|
||||
return vec3;
|
||||
|
||||
if (lockWholeAvatar && 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 +512,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,53 +519,65 @@ 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());
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
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);
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
return;
|
||||
}
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
|
||||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion inv_quat;
|
||||
LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
|
||||
switch (deflectionStyle)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
oppositeJointPose->cloneRotationFrom(jointPose);
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
|
||||
break;
|
||||
|
||||
case SYMPATHETIC_DELTA:
|
||||
oppositeJointPose->setPublicRotation(deltaRot * oppositeJointPose->getPublicRotation());
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
|
||||
break;
|
||||
|
||||
case MIRROR:
|
||||
oppositeJointPose->mirrorRotationFrom(jointPose);
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
break;
|
||||
|
||||
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());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -757,11 +879,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 +923,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 +1020,118 @@ 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;
|
||||
}
|
||||
|
||||
void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotationChange)
|
||||
{
|
||||
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(joint->jointName());
|
||||
if (!parentJoint)
|
||||
return;
|
||||
|
||||
LLJoint* pJoint = parentJoint->getJointState()->getJoint();
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, pJoint->getWorldRotation(), rotationChange);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotatedParentWorldRot, LLQuaternion rotationChange)
|
||||
{
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
if (jointPose->getWorldRotationLockState())
|
||||
{
|
||||
LLQuaternion worldRotOfThisJoint = jointPose->getJointState()->getJoint()->getWorldRotation();
|
||||
LLQuaternion differenceInWorldRot = worldRotOfThisJoint * ~rotatedParentWorldRot;
|
||||
LLQuaternion rotDiffInChildFrame = differenceInWorldRot * rotationChange * ~differenceInWorldRot;
|
||||
rotDiffInChildFrame.conjugate();
|
||||
|
||||
jointPose->setPublicRotation(false, rotDiffInChildFrame * jointPose->getPublicRotation());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, rotatedParentWorldRot, rotationChange);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
|
||||
{
|
||||
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
|
||||
{
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
if (jointPose->getWorldRotationLockState())
|
||||
{
|
||||
redo ? jointPose->redoLastChange() : jointPose->undoLastChange();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ public:
|
|||
/// </summary>
|
||||
/// <param name="jointName">The name of the joint to match.</param>
|
||||
/// <returns>The matching joint if found, otherwise nullptr</returns>
|
||||
const FSPoserJoint* getPoserJointByName(const std::string& jointName);
|
||||
const FSPoserJoint* getPoserJointByName(const std::string& jointName) const;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to start posing the supplied avatar.
|
||||
|
|
@ -492,6 +492,19 @@ public:
|
|||
/// <returns>The rotation of the requested joint, if determinable, otherwise a default vector.</returns>
|
||||
LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation of a joint for the supplied avatar for export.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose joint is being queried.</param>
|
||||
/// <param name="joint">The joint to determine the rotation for.</param>
|
||||
/// <param name="lockWholeAvatar">Whether the whole avatar should be rotation/position locked in the BVH export.</param>
|
||||
/// <returns>The rotation of the requested joint for export.</returns>
|
||||
/// <remarks>
|
||||
/// 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'.
|
||||
/// </remarks>
|
||||
LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the rotation of a joint for the supplied avatar.
|
||||
/// </summary>
|
||||
|
|
@ -559,8 +572,9 @@ public:
|
|||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose joint is to be recaptured.</param>
|
||||
/// <param name="joint">The joint to recapture.</param>
|
||||
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
|
||||
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
|
||||
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style);
|
||||
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style);
|
||||
|
||||
/// <summary>
|
||||
/// Sets all of the joint rotations of the supplied avatar to zero.
|
||||
|
|
@ -574,7 +588,34 @@ public:
|
|||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <returns>True if the supplied joint has a 'base' rotation of zero (thus user-supplied change only), otherwise false.</returns>
|
||||
bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
bool userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied joints position will be set in an export.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <returns>True if the export will 'lock' the joint, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// BVH import leaves a joint 'free' if its rotation is less than something arbitrary.
|
||||
/// </remarks>
|
||||
bool exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied joint for the supplied avatar is rotationally locked to the world.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <returns>True if the joint is maintaining a fixed-rotation in world, otherwise false.</returns>
|
||||
bool getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the world-rotation-lock status for supplied joint for the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <param name="newState">The lock state to apply.</param>
|
||||
void setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
|
||||
|
|
@ -692,6 +733,53 @@ public:
|
|||
/// <returns>True if the avatar is safe to manipulate, otherwise false.</returns>
|
||||
bool isAvatarSafeToUse(LLVOAvatar* avatar) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the depth of descendant joints for the supplied joint.
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint to determine the depth for.</param>
|
||||
/// <param name="depth">The depth of the supplied joint.</param>
|
||||
/// <returns>The number of generations of descendents the joint has, if none, then zero.</returns>
|
||||
int getChildJointDepth(const FSPoserJoint* joint, int depth) const;
|
||||
|
||||
/// <summary>
|
||||
/// Derotates the first world-locked child joint to the supplied joint.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="rotation">The rotation the supplied joint was/is being changed by.</param>
|
||||
/// <remarks>
|
||||
/// There are two ways to resolve this problem: before the rotation is applied in the PosingMotion (the animation) or after.
|
||||
/// If performed after, a feedback loop is created, because you're noting the world-rotation in one frame, then correcting it back to that in another.
|
||||
/// This implementation works by applying an opposing-rotation to the locked child joint which is corrected for the relative world-rotations of parent and child.
|
||||
/// </remarks>
|
||||
void deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies a de-rotation if it is world-locked.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="parentWorldRot">The world-rotation of the joint that was edited.</param>
|
||||
/// <param name="rotation">The rotation the joint was edit is being changed by.</param>
|
||||
void deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion parentWorldRot,
|
||||
LLQuaternion rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an undo or redo of an edit to the supplied joints world-locked descendants.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
|
||||
void undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
|
||||
|
||||
/// <summary>
|
||||
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies an undo or redo if it is world-locked.
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint which will have the undo or redo performed, if it is world locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
|
||||
void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
|
|
|
|||
|
|
@ -259,11 +259,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
|
||||
{
|
||||
joint->zeroBaseRotation(lockInBvh);
|
||||
}
|
||||
|
||||
bool FSPosingMotion::vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const
|
||||
{
|
||||
if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) &&
|
||||
|
|
|
|||
|
|
@ -121,8 +121,17 @@ public:
|
|||
/// <summary>
|
||||
/// Sets all of the non-Collision Volume base-and-delta rotations to zero, and clears the undo/redo queue.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default, sets the joint to lock in BVH export.
|
||||
/// </remarks>
|
||||
void setAllRotationsToZeroAndClearUndo();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the BVH export state for the supplied joint.
|
||||
/// </summary>
|
||||
/// <param name="lockInBvh">Whether the joint should be locked if exported to BVH.</param>
|
||||
void setJointBvhLock(FSJointPose* joint, bool lockInBvh);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// The axial difference considered close enough to be the same.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ width="430">
|
|||
<string name="icon_category" translate="false">Inv_BodyShape</string>
|
||||
<string name="icon_bone" translate="false"></string>
|
||||
<string name="icon_object" translate="false">Inv_Object</string>
|
||||
<string name="icon_rotation_bvh_locked_edited" translate="false">Script_Running</string>
|
||||
<string name="icon_rotation_bvh_locked_unedited" translate="false">Script_NotRunning</string>
|
||||
<string name="icon_rotation_bvh_unlocked" translate="false">Script_Error</string>
|
||||
<string name="icon_rotation_is_world_locked" translate="false">Locked_Icon</string>
|
||||
<string name="icon_rotation_does_not_export" translate="false">Conv_toolbar_close</string>
|
||||
<string name="icon_rotation_is_own_work" translate="false">Check_Mark</string>
|
||||
<string name="icon_save_button" translate="false">Icon_Dock_Foreground</string>
|
||||
<string name="icon_save_failed_button" translate="false">Parcel_Exp_Color</string>
|
||||
|
|
@ -548,6 +553,17 @@ width="430">
|
|||
function="Poser.CommitSpinner"
|
||||
parameter="2"/>
|
||||
</spinner>
|
||||
<check_box
|
||||
control_name="FSPoserPelvisUnlockedForBvhSave"
|
||||
name="unlock_pelvis_for_bvh_save_checkbox"
|
||||
visible="false"
|
||||
height="16"
|
||||
label="Unlock Pelvis in BVH"
|
||||
follows="left|top"
|
||||
left="5"
|
||||
tool_tip="When you save this pose to BVH, the Pelvis will not be 'locked'. This means other animations in-world can 'move' the whole avatar. Use this if your pose is meant to play 'on top of' another pose."
|
||||
top_pad="15"
|
||||
width="134" />
|
||||
<!-- to make this panel behaves like the others in code-behind, it has an invisible list -->
|
||||
<scroll_list
|
||||
visible="false"
|
||||
|
|
@ -965,26 +981,15 @@ 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" />
|
||||
<check_box
|
||||
control_name="FSPoserResetBaseRotationOnEdit"
|
||||
name="reset_base_rotation_on_edit_checkbox"
|
||||
height="16"
|
||||
label="Reset base-rotation on edit"
|
||||
follows="left|top"
|
||||
left="5"
|
||||
tool_tip="When you first edit a rotation, reset it to zero. This means your work can save a pose (and not a diff see load/save). A green tick appears next each joint you have zero-ed export."
|
||||
top_pad="5"
|
||||
width="134" />
|
||||
<check_box
|
||||
control_name="FSPoserSaveExternalFileAlso"
|
||||
name="also_save_bvh_checkbox"
|
||||
height="16"
|
||||
enabled="false"
|
||||
label="Write BVH when saving**"
|
||||
label="Write BVH when saving"
|
||||
follows="left|top"
|
||||
left="15"
|
||||
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"
|
||||
left="5"
|
||||
tool_tip="When you save your pose, also write a BVH file. BVH 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="5"
|
||||
width="134" />
|
||||
<check_box
|
||||
control_name="FSPoserOnSaveConfirmOverwrite"
|
||||
|
|
@ -1005,7 +1010,7 @@ width="430">
|
|||
left="5"
|
||||
tool_tip="The skeleton has unnatural joint rotations by default. This complicates posing. When checked, the joints will rotate in a more natural way."
|
||||
top_pad="5"
|
||||
width="134" />
|
||||
width="134" />
|
||||
<check_box
|
||||
control_name="FSManipShowJointMarkers"
|
||||
name="show_joint_markers_checkbox"
|
||||
|
|
@ -1015,7 +1020,17 @@ width="430">
|
|||
left="5"
|
||||
tool_tip="Show small indicators to aid joint selection when visually posing."
|
||||
top_pad="5"
|
||||
width="134" />
|
||||
width="134" />
|
||||
<check_box
|
||||
control_name="RenderDepthOfFieldInEditMode"
|
||||
name="dof_edit_mode_checkbox"
|
||||
height="16"
|
||||
label="Enable DoF in Edit Mode"
|
||||
follows="left|top"
|
||||
left="5"
|
||||
tool_tip="When using the Manipulator, Depth of Field normally disables automatically. This keeps Depth of Field on when editing poses or prims."
|
||||
top_pad="5"
|
||||
width="134" />
|
||||
</panel>
|
||||
</tab_container>
|
||||
<button
|
||||
|
|
@ -1556,6 +1571,20 @@ width="430">
|
|||
top_delta="0"
|
||||
left_pad="1">
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="21"
|
||||
layout="topleft"
|
||||
image_overlay="Locked_Icon"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
name="toggle_LockWorldRotation"
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
tool_tip="Lock rotation in the world for the selected limb(s). Limbs that are world-locked keep the same rotation in-world when their parent-limbs move. Eg: lock your eyes and turn your head, your eyes keep looking (mostly) in the same direction."
|
||||
width="18" >
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="21"
|
||||
|
|
@ -1573,7 +1602,7 @@ width="430">
|
|||
tool_tip="Mirror changes to the opposite joint."
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="52" >
|
||||
width="43" >
|
||||
<button.commit_callback
|
||||
function="Poser.ToggleMirrorChanges"/>
|
||||
</button>
|
||||
|
|
@ -1594,7 +1623,7 @@ width="430">
|
|||
tool_tip="Copy changes to the opposite joint."
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="52" >
|
||||
width="43" >
|
||||
<button.commit_callback
|
||||
function="Poser.ToggleSympatheticChanges"/>
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue