FIRE-34884: Rework delta mode

to break gimbal lock for selected lock-prone joints
master
Angeldark Raymaker 2024-11-30 14:46:40 +00:00
parent 5d4839146e
commit c01fe3379e
5 changed files with 158 additions and 131 deletions

View File

@ -52,6 +52,7 @@ constexpr std::string_view POSE_PRESETS_HANDS_SUBDIRECTORY = "hand_presets";
constexpr char XML_LIST_HEADER_STRING_PREFIX[] = "header_";
constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_";
constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_";
constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_rotate_";
constexpr std::string_view POSER_ADVANCEDWINDOWSTATE_SAVE_KEY = "FSPoserAdvancedWindowState";
constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed";
@ -76,7 +77,6 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastRotation(); });
mCommitCallbackRegistrar.add("Poser.ToggleMirrorChanges", [this](LLUICtrl*, const LLSD&) { onToggleMirrorChange(); });
mCommitCallbackRegistrar.add("Poser.ToggleSympatheticChanges", [this](LLUICtrl*, const LLSD&) { onToggleSympatheticChange(); });
mCommitCallbackRegistrar.add("Poser.ToggleDeltaModeChanges", [this](LLUICtrl*, const LLSD &) { onToggleDeltaModeChange(); });
mCommitCallbackRegistrar.add("Poser.AdjustTrackPadSensitivity", [this](LLUICtrl*, const LLSD&) { onAdjustTrackpadSensitivity(); });
mCommitCallbackRegistrar.add("Poser.PositionSet", [this](LLUICtrl*, const LLSD&) { onAvatarPositionSet(); });
@ -110,20 +110,20 @@ bool FSFloaterPoser::postBuild()
mAvatarTrackball->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbTrackballChanged(); });
mLimbYawSlider = getChild<LLSliderCtrl>("limb_yaw");
mLimbYawSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbYawPitchRollChanged(); });
mLimbYawSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mLimbPitchSlider = getChild<LLSliderCtrl>("limb_pitch");
mLimbPitchSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbYawPitchRollChanged(); });
mLimbPitchSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mLimbRollSlider = getChild<LLSliderCtrl>("limb_roll");
mLimbRollSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbYawPitchRollChanged(); });
mLimbRollSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mJointsTabs = getChild<LLTabContainer>("joints_tabs");
mJointsTabs->setCommitCallback(
[this](LLUICtrl*, const LLSD&)
{
onJointTabSelect();
setRotationChangeButtons(false, false, false);
setRotationChangeButtons(false, false);
});
mAvatarSelectionScrollList = getChild<LLScrollListCtrl>("avatarSelection_scroll");
@ -1077,30 +1077,22 @@ void FSFloaterPoser::onToggleLoadSavePanel()
void FSFloaterPoser::onToggleMirrorChange()
{
setRotationChangeButtons(true, false, false);
setRotationChangeButtons(true, false);
}
void FSFloaterPoser::onToggleSympatheticChange()
{
setRotationChangeButtons(false, true, false);
setRotationChangeButtons(false, true);
}
void FSFloaterPoser::onToggleDeltaModeChange()
void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool togglingSympathetic)
{
setRotationChangeButtons(false, false, true);
}
void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool togglingSympathetic, bool togglingDelta)
{
if (togglingSympathetic || togglingDelta)
if (togglingSympathetic)
mToggleMirrorRotationBtn->setValue(false);
if (togglingMirror || togglingDelta)
if (togglingMirror)
mToggleSympatheticRotationBtn->setValue(false);
if (togglingMirror || togglingSympathetic)
mToggleDeltaModeBtn->setValue(false);
refreshTrackpadCursor();
}
@ -1121,7 +1113,7 @@ void FSFloaterPoser::onUndoLastRotation()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.undoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
enableOrDisableRedoButton();
@ -1146,7 +1138,7 @@ void FSFloaterPoser::onUndoLastPosition()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.undoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedPositionSliders();
@ -1170,7 +1162,7 @@ void FSFloaterPoser::onUndoLastScale()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.undoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedScaleSliders();
@ -1210,7 +1202,7 @@ void FSFloaterPoser::onResetPosition()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.resetJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.resetJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedPositionSliders();
@ -1237,7 +1229,7 @@ void FSFloaterPoser::onResetScale()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.resetJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.resetJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedScaleSliders();
@ -1260,7 +1252,7 @@ void FSFloaterPoser::onRedoLastRotation()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.redoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
enableOrDisableRedoButton();
@ -1285,7 +1277,7 @@ void FSFloaterPoser::onRedoLastPosition()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.redoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedPositionSliders();
@ -1309,7 +1301,7 @@ void FSFloaterPoser::onRedoLastScale()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.redoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle(item->jointName()));
}
refreshAdvancedScaleSliders();
@ -1383,6 +1375,7 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
if (activeTab == mPositionRotationPnl)
{
mEntireAvJointScroll->selectFirstItem();
scrollList = mEntireAvJointScroll;
}
else if (activeTab == mBodyJointsPnl)
@ -1432,18 +1425,47 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
return joints;
}
E_BoneDeflectionStyles FSFloaterPoser::getUiSelectedBoneDeflectionStyle() const
bool FSFloaterPoser::isAnyDeltaModeRotation(const E_BoneDeflectionStyles deflection)
{
if (mToggleDeltaModeBtn->getValue().asBoolean())
return true;
switch (deflection)
{
case DELTAMODE:
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
return true;
case NONE:
case SYMPATHETIC:
case MIRROR:
default:
return false;
}
}
E_BoneDeflectionStyles FSFloaterPoser::getUiSelectedBoneDeflectionStyle(const std::string& jointName) const
{
if (jointName.empty())
return NONE;
bool isDelta = mToggleDeltaModeBtn->getValue().asBoolean();
bool hasRotationStylePreferenceParameter = hasString(XML_JOINT_DELTAROT_STRING_PREFIX + jointName);
if (hasRotationStylePreferenceParameter)
{
std::string paramValue = getString(XML_JOINT_DELTAROT_STRING_PREFIX + jointName);
if (strstr(paramValue.c_str(), "true"))
isDelta = true;
}
if (mToggleMirrorRotationBtn->getValue().asBoolean())
return MIRROR;
return isDelta ? MIRROR_DELTA : MIRROR;
if (mToggleSympatheticRotationBtn->getValue().asBoolean())
return SYMPATHETIC;
return isDelta ? SYMPATHETIC_DELTA : SYMPATHETIC;
if (mToggleDeltaModeBtn->getValue().asBoolean())
return DELTAMODE;
return NONE;
return isDelta ? DELTAMODE : NONE;
}
LLVOAvatar* FSFloaterPoser::getUiSelectedAvatar() const
@ -1525,9 +1547,9 @@ LLVOAvatar* FSFloaterPoser::getAvatarByUuid(const LLUUID& avatarToFind) const
void FSFloaterPoser::onAdvancedPositionSet()
{
F32 posX = (F32)mAdvPosXSlider->getValue().asReal();
F32 posY = (F32)mAdvPosYSlider->getValue().asReal();
F32 posZ = (F32)mAdvPosZSlider->getValue().asReal();
F32 posX = mAdvPosXSlider->getValueF32();
F32 posY = mAdvPosYSlider->getValueF32();
F32 posZ = mAdvPosZSlider->getValueF32();
setSelectedJointsPosition(posX, posY, posZ);
refreshAvatarPositionSliders();
@ -1535,18 +1557,18 @@ void FSFloaterPoser::onAdvancedPositionSet()
void FSFloaterPoser::onAdvancedScaleSet()
{
F32 scX = (F32)mAdvScaleXSlider->getValue().asReal();
F32 scY = (F32)mAdvScaleYSlider->getValue().asReal();
F32 scZ = (F32)mAdvScaleZSlider->getValue().asReal();
F32 scX = mAdvScaleXSlider->getValueF32();
F32 scY = mAdvScaleYSlider->getValueF32();
F32 scZ = mAdvScaleZSlider->getValueF32();
setSelectedJointsScale(scX, scY, scZ);
}
void FSFloaterPoser::onAvatarPositionSet()
{
F32 posX = (F32)mPosXSlider->getValue().asReal();
F32 posY = (F32)mPosYSlider->getValue().asReal();
F32 posZ = (F32)mPosZSlider->getValue().asReal();
F32 posX = mPosXSlider->getValueF32();
F32 posY = mPosYSlider->getValueF32();
F32 posZ = mPosZSlider->getValueF32();
setSelectedJointsPosition(posX, posY, posZ);
refreshAdvancedPositionSliders();
@ -1554,55 +1576,40 @@ void FSFloaterPoser::onAvatarPositionSet()
void FSFloaterPoser::onLimbTrackballChanged()
{
LLVector3 trackPadPos;
LLSD position = mAvatarTrackball->getValue();
if (position.isArray() && position.size() == 3)
LLVector3 trackPadPos, trackPadDeltaPos;
LLSD position = mAvatarTrackball->getValue();
LLSD deltaPosition = mAvatarTrackball->getValueDelta();
if (position.isArray() && position.size() == 3 && deltaPosition.isArray() && deltaPosition.size() == 3)
{
trackPadPos.setValue(position);
trackPadDeltaPos.setValue(deltaPosition);
}
else
return;
F32 yaw, pitch, roll;
yaw = trackPadPos.mV[VX];
pitch = trackPadPos.mV[VY];
roll = trackPadPos.mV[VZ];
F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f);
yaw *= trackPadSensitivity;
pitch *= trackPadSensitivity;
yaw = unWrapScale(yaw) * NormalTrackpadRangeInRads;
pitch = unWrapScale(pitch) * NormalTrackpadRangeInRads;
roll = unWrapScale(roll) * NormalTrackpadRangeInRads;
trackPadPos.mV[VX] *= trackPadSensitivity;
trackPadPos.mV[VY] *= trackPadSensitivity;
if (mToggleDeltaModeBtn->getValue().asBoolean())
{
F32 deltaYaw, deltaPitch, deltaRoll;
LLSD deltaPosition = mAvatarTrackball->getValueDelta();
LLVector3 trackPadDeltaPos;
if (deltaPosition.isArray() && deltaPosition.size() == 3)
{
trackPadDeltaPos.setValue(deltaPosition);
deltaYaw = trackPadDeltaPos[VX] * NormalTrackpadRangeInRads;
deltaPitch = trackPadDeltaPos[VY] * NormalTrackpadRangeInRads;
deltaRoll = trackPadDeltaPos[VZ] * NormalTrackpadRangeInRads;
deltaYaw *= trackPadSensitivity;
deltaPitch *= trackPadSensitivity;
setSelectedJointsRotation(deltaYaw, deltaPitch, deltaRoll);
}
}
else
{
setSelectedJointsRotation(yaw, pitch, roll);
}
trackPadPos.mV[VX] = unWrapScale(trackPadPos.mV[VX]) * NormalTrackpadRangeInRads;
trackPadPos.mV[VY] = unWrapScale(trackPadPos.mV[VY]) * NormalTrackpadRangeInRads;
trackPadPos.mV[VZ] = unWrapScale(trackPadPos.mV[VZ]) * NormalTrackpadRangeInRads;
trackPadDeltaPos[VX] *= NormalTrackpadRangeInRads * trackPadSensitivity;
trackPadDeltaPos[VY] *= NormalTrackpadRangeInRads * trackPadSensitivity;
trackPadDeltaPos[VZ] *= NormalTrackpadRangeInRads;
setSelectedJointsRotation(trackPadPos, trackPadDeltaPos);
// WARNING!
// as tempting as it is to refactor the following to refreshRotationSliders(), don't.
// getRotationOfFirstSelectedJoint/setSelectedJointsRotation are
// not necessarily symmetric functions (see their remarks).
mLimbYawSlider->setValue(yaw *= RAD_TO_DEG);
mLimbPitchSlider->setValue(pitch *= RAD_TO_DEG);
mLimbRollSlider->setValue(roll *= RAD_TO_DEG);
mLimbYawSlider->setValue(trackPadPos.mV[VX] *= RAD_TO_DEG);
mLimbPitchSlider->setValue(trackPadPos.mV[VY] *= RAD_TO_DEG);
mLimbRollSlider->setValue(trackPadPos.mV[VZ] *= RAD_TO_DEG);
}
F32 FSFloaterPoser::unWrapScale(F32 scale)
@ -1619,52 +1626,44 @@ F32 FSFloaterPoser::unWrapScale(F32 scale)
return result;
}
void FSFloaterPoser::onLimbYawPitchRollChanged()
void FSFloaterPoser::onYawPitchRollSliderChanged()
{
F32 yaw = (F32)mLimbYawSlider->getValue().asReal();
F32 pitch = (F32)mLimbPitchSlider->getValue().asReal();
F32 roll = (F32)mLimbRollSlider->getValue().asReal();
LLVector3 absoluteRotation, deltaRotation;
absoluteRotation.mV[VX] = mLimbYawSlider->getValueF32() * DEG_TO_RAD;
absoluteRotation.mV[VY] = mLimbPitchSlider->getValueF32() * DEG_TO_RAD;
absoluteRotation.mV[VZ] = mLimbRollSlider->getValueF32() * DEG_TO_RAD;
yaw *= DEG_TO_RAD;
pitch *= DEG_TO_RAD;
roll *= DEG_TO_RAD;
deltaRotation = absoluteRotation - mLastSliderRotation;
mLastSliderRotation = absoluteRotation;
setSelectedJointsRotation(yaw, pitch, roll);
setSelectedJointsRotation(absoluteRotation, deltaRotation);
// WARNING!
// as tempting as it is to refactor the following to refreshTrackpadCursor(), don't.
// getRotationOfFirstSelectedJoint/setSelectedJointsRotation are
// not necessarily symmetric functions (see their remarks).
F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f);
yaw /= trackPadSensitivity;
pitch /= trackPadSensitivity;
absoluteRotation.mV[VX] /= trackPadSensitivity;
absoluteRotation.mV[VY] /= trackPadSensitivity;
yaw /= NormalTrackpadRangeInRads;
pitch /= NormalTrackpadRangeInRads;
roll /= NormalTrackpadRangeInRads;
absoluteRotation.mV[VX] /= NormalTrackpadRangeInRads;
absoluteRotation.mV[VY] /= NormalTrackpadRangeInRads;
absoluteRotation.mV[VZ] /= NormalTrackpadRangeInRads;
mAvatarTrackball->setValue(yaw, pitch, roll);
mAvatarTrackball->setValue(absoluteRotation.getValue());
}
void FSFloaterPoser::onAdjustTrackpadSensitivity()
{
gSavedSettings.setF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY, (F32)mTrackpadSensitivitySlider->getValue().asReal());
gSavedSettings.setF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY, mTrackpadSensitivitySlider->getValueF32());
refreshTrackpadCursor();
}
void FSFloaterPoser::refreshTrackpadCursor()
{
F32 axis1 = (F32)mLimbYawSlider->getValue().asReal();
F32 axis2 = (F32)mLimbPitchSlider->getValue().asReal();
F32 axis3 = (F32)mLimbRollSlider->getValue().asReal();
axis1 *= DEG_TO_RAD;
axis2 *= DEG_TO_RAD;
axis3 *= DEG_TO_RAD;
axis1 /= NormalTrackpadRangeInRads;
axis2 /= NormalTrackpadRangeInRads;
axis3 /= NormalTrackpadRangeInRads;
F32 axis1 = mLimbYawSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis2 = mLimbPitchSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis3 = mLimbRollSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f);
axis1 /= trackPadSensitivity;
@ -1695,6 +1694,7 @@ void FSFloaterPoser::refreshRotationSliders()
{
LLVector3 rotation = getRotationOfFirstSelectedJoint();
mLastSliderRotation = rotation;
mLimbYawSlider->setValue(rotation.mV[VX] *= RAD_TO_DEG);
mLimbPitchSlider->setValue(rotation.mV[VY] *= RAD_TO_DEG);
mLimbRollSlider->setValue(rotation.mV[VZ] *= RAD_TO_DEG);
@ -1727,8 +1727,7 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
LLVector3 vec3 = LLVector3(x, y, z);
LLVector3 vec3 = LLVector3(x, y, z);
for (auto item : getUiSelectedPoserJoints())
{
@ -1736,11 +1735,12 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
if (!currentlyPosingJoint)
continue;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName());
mPoserAnimator.setJointPosition(avatar, item, vec3, defl);
}
}
void FSFloaterPoser::setSelectedJointsRotation(F32 yawInRadians, F32 pitchInRadians, F32 rollInRadians)
void FSFloaterPoser::setSelectedJointsRotation(LLVector3 absoluteRot, LLVector3 deltaRot)
{
LLVOAvatar *avatar = getUiSelectedAvatar();
if (!avatar)
@ -1749,10 +1749,8 @@ void FSFloaterPoser::setSelectedJointsRotation(F32 yawInRadians, F32 pitchInRadi
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
LLVector3 vec3 = LLVector3(yawInRadians, pitchInRadians, rollInRadians);
auto selectedJoints = getUiSelectedPoserJoints();
bool savingToExternal = getWhetherToResetBaseRotationOnEdit();
auto selectedJoints = getUiSelectedPoserJoints();
bool savingToExternal = getWhetherToResetBaseRotationOnEdit();
for (auto item : selectedJoints)
{
@ -1770,8 +1768,9 @@ void FSFloaterPoser::setSelectedJointsRotation(F32 yawInRadians, F32 pitchInRadi
continue;
}
mPoserAnimator.setJointRotation(avatar, item, vec3, defl, getJointTranslation(item->jointName()),
getJointNegation(item->jointName()), savingToExternal);
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName());
mPoserAnimator.setJointRotation(avatar, item, isAnyDeltaModeRotation(defl) ? deltaRot : absoluteRot, defl,
getJointTranslation(item->jointName()), getJointNegation(item->jointName()), savingToExternal);
}
if (savingToExternal)
@ -1787,8 +1786,7 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
LLVector3 vec3 = LLVector3(x, y, z);
LLVector3 vec3 = LLVector3(x, y, z);
for (auto item : getUiSelectedPoserJoints())
{
@ -1796,6 +1794,7 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
if (!currentlyPosingJoint)
continue;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(item->jointName());
mPoserAnimator.setJointScale(avatar, item, vec3, defl);
}
}

View File

@ -147,8 +147,16 @@ class FSFloaterPoser : public LLFloater
/// Gets the current bone-deflection style: encapsulates 'anything else you want to do' while you're manipulating a joint.
/// Such as: fiddle the opposite joint too.
/// </summary>
/// <param name="jointName">The well-known joint name of the joint to add the row for, eg: mChest.</param>
/// <returns>A E_BoneDeflectionStyles member.</returns>
E_BoneDeflectionStyles getUiSelectedBoneDeflectionStyle() const;
E_BoneDeflectionStyles getUiSelectedBoneDeflectionStyle(const std::string& jointName) const;
/// <summary>
/// Gets whether the supplied joint name should be rotated using the delta method.
/// </summary>
/// <param name="deflection">The deflection to consider.</param>
/// <returns>true if the joint should be rotated by delta for any reason, otherwise false.</returns>
bool isAnyDeltaModeRotation(const E_BoneDeflectionStyles deflection);
/// <summary>
/// Gets the collection of UUIDs for nearby avatars.
@ -178,7 +186,7 @@ class FSFloaterPoser : public LLFloater
/// There may be +/- PI difference two axes, because harmonics.
/// Thus keep your UI synced with less gets.
/// </remarks>
void setSelectedJointsRotation(F32 yawInRadians, F32 pitchInRadians, F32 rollInRadians);
void setSelectedJointsRotation(LLVector3 absoluteRot, LLVector3 deltaRot);
void setSelectedJointsPosition(F32 x, F32 y, F32 z);
void setSelectedJointsScale(F32 x, F32 y, F32 z);
@ -214,8 +222,7 @@ class FSFloaterPoser : public LLFloater
void onToggleAdvancedPanel();
void onToggleMirrorChange();
void onToggleSympatheticChange();
void onToggleDeltaModeChange();
void setRotationChangeButtons(bool mirror, bool sympathetic, bool togglingDelta);
void setRotationChangeButtons(bool mirror, bool sympathetic);
void onUndoLastRotation();
void onRedoLastRotation();
void onUndoLastPosition();
@ -230,7 +237,7 @@ class FSFloaterPoser : public LLFloater
void startPosingSelf();
void stopPosingSelf();
void onLimbTrackballChanged();
void onLimbYawPitchRollChanged();
void onYawPitchRollSliderChanged();
void onAvatarPositionSet();
void onAdvancedPositionSet();
void onAdvancedScaleSet();
@ -369,6 +376,7 @@ class FSFloaterPoser : public LLFloater
/// </remarks>
static F32 unWrapScale(F32 scale);
LLVector3 mLastSliderRotation;
FSVirtualTrackpad* mAvatarTrackball{ nullptr };
LLSliderCtrl* mTrackpadSensitivitySlider{ nullptr };

View File

@ -508,10 +508,15 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
jointPose->setRotationDelta(rot_quat);
break;
case DELTAMODE:
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
jointPose->setRotationDelta(rot_quat * jointPose->getRotationDelta());
break;
case DELTAMODE:
jointPose->setRotationDelta(rot_quat * jointPose->getRotationDelta());
return;
case NONE:
default:
jointPose->setRotationDelta(rot_quat);
@ -532,13 +537,18 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
oppositeJointPose->setRotationDelta(rot_quat);
break;
case SYMPATHETIC_DELTA:
oppositeJointPose->setRotationDelta(rot_quat * oppositeJointPose->getRotationDelta());
break;
case MIRROR:
inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
oppositeJointPose->setRotationDelta(inv_quat);
break;
case DELTAMODE:
oppositeJointPose->setRotationDelta(rot_quat * oppositeJointPose->getRotationDelta());
case MIRROR_DELTA:
inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
oppositeJointPose->setRotationDelta(inv_quat * oppositeJointPose->getRotationDelta());
break;
default:

View File

@ -50,10 +50,12 @@ typedef enum E_BoneTypes
/// </summary>
typedef enum E_BoneDeflectionStyles
{
NONE = 0, // do nothing additional
MIRROR = 1, // change the other joint, like in a mirror, eg: one left one right
SYMPATHETIC = 2, // change the other joint, but opposite to a mirrored way, eg: both go right or both go left
DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current
NONE = 0, // do nothing additional
MIRROR = 1, // change the other joint, like in a mirror, eg: one left one right
SYMPATHETIC = 2, // change the other joint, but opposite to a mirrored way, eg: both go right or both go left
DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current
MIRROR_DELTA = 4, // As MIRROR, but applied as negated delta to opposite
SYMPATHETIC_DELTA = 5, // As SYMPATHETIC, but applied as delta
} E_BoneDeflectionStyles;
/// <summary>

View File

@ -113,6 +113,16 @@ width="403">
<string name="joint_transform_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<!-- For the joints/bones/thingos that are apt to Gimbal Lock, we may instead apply rotations as deltas by default, which never lock -->
<string name="joint_delta_rotate_mHipLeft">true</string>
<string name="joint_delta_rotate_mHipRight">true</string>
<string name="joint_delta_rotate_mKneeLeft">true</string>
<string name="joint_delta_rotate_mKneeRight">true</string>
<string name="joint_delta_rotate_mElbowLeft">true</string>
<string name="joint_delta_rotate_mElbowRight">true</string>
<string name="joint_delta_rotate_mWristLeft">true</string>
<string name="joint_delta_rotate_mWristRight">true</string>
<!-- A matching bone name with the 'header_' prefix produces a sub-heading with the supplied name -->
<string name="header_mHead">Body</string>
<string name="header_mFaceForeheadLeft">Forehead/Brows</string>
@ -913,12 +923,10 @@ width="403">
image_overlay="Inv_Mesh"
image_unselected="Toolbar_Middle_Off"
name="delta_mode_toggle"
tool_tip="If changing multiple joints each changes by the same amount, rather than all of them getting the same rotation."
tool_tip="If changing multiple joints each changes by the same amount, rather than all of them getting the same rotation. Also used for breaking Gimbal Lock."
width="18"
top_delta="0"
left_pad="1">
<button.commit_callback
function="Poser.ToggleDeltaModeChanges"/>
</button>
<button
follows="left|top"