# Conflicts:
#	indra/newview/skins/default/xui/de/panel_preferences_graphics1.xml
master
Ansariel 2024-11-20 19:07:16 +01:00
commit f0690edc99
23 changed files with 852 additions and 767 deletions

View File

@ -687,7 +687,7 @@ void LLStatBar::drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect )
{
// <FS:Beq> FIRE-33481 - FS hangs on login, progress bar full
LL_DEBUGS("STATBAR") << "name: " << this->getName() << "min: " << min << ", max: " << max << ", value_scale: " << value_scale << LL_ENDL;
if ( value_scale == INFINITY )
if ( value_scale == std::numeric_limits<float>::infinity() )
{
return;
}

View File

@ -140,6 +140,7 @@ set(viewer_SOURCE_FILES
fsfloatervramusage.cpp
fsfloaterwearablefavorites.cpp
fsfloaterwhitelisthelper.cpp
fsjointpose.cpp
fskeywords.cpp
fslslbridge.cpp
fslslbridgerequest.cpp
@ -937,6 +938,7 @@ set(viewer_HEADER_FILES
fsfloaternearbychat.h
fsfloaterpartialinventory.h
fsfloaterplacedetails.h
fsfloaterposer.h
fsfloaterposestand.h
fsfloaterprotectedfolders.h
fsfloaterradar.h
@ -949,6 +951,7 @@ set(viewer_HEADER_FILES
fsfloatervramusage.h
fsfloaterwearablefavorites.h
fsfloaterwhitelisthelper.h
fsjointpose.h
fsgridhandler.h
fskeywords.h
fslslbridge.h
@ -970,6 +973,8 @@ set(viewer_HEADER_FILES
fspanelradar.h
fsparticipantlist.h
fspose.h
fsposeranimator.h
fsposingmotion.h
fsradar.h
fsradarentry.h
fsradarlistctrl.h
@ -979,6 +984,7 @@ set(viewer_HEADER_FILES
fsscrolllistctrl.h
fsslurl.h
fsslurlcommand.h
fsvirtualtrackpad.h
fsworldmapmessage.h
lggbeamcolormapfloater.h
lggbeammapfloater.h

View File

@ -8067,6 +8067,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPoserResetBaseRotationOnEdit</key>
<map>
<key>Comment</key>
<string>Whether to reset the base-rotation of a joint to zero when a user edits it.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPoserStopPosingWhenClosed</key>
<map>
<key>Comment</key>

View File

@ -54,6 +54,7 @@ constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transfor
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";
constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit";
} // namespace
/// <summary>
@ -61,7 +62,6 @@ constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPos
/// The trackpad ordinarily has a range of +1..-1; multiplied by PI, gives PI to -PI, or all 360 degrees of deflection.
/// </summary>
constexpr F32 NormalTrackpadRangeInRads = F_PI;
bool FSFloaterPoser::sDisableRecaptureUntilStopPosing;
FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
{
@ -117,12 +117,11 @@ bool FSFloaterPoser::postBuild()
mLimbRollSlider = getChild<LLSliderCtrl>("limb_roll");
mLimbRollSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbYawPitchRollChanged(); });
// find-and-binds
mJointsTabs = getChild<LLTabContainer>("joints_tabs");
mJointsTabs->setCommitCallback(
[this](LLUICtrl*, const LLSD&)
{
onJointSelect();
onJointTabSelect();
setRotationChangeButtons(false, false, false);
});
@ -132,23 +131,23 @@ bool FSFloaterPoser::postBuild()
mBodyJointsScrollList = getChild<LLScrollListCtrl>("body_joints_scroll");
mBodyJointsScrollList->setCommitOnSelectionChange(true);
mBodyJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointSelect(); });
mBodyJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointTabSelect(); });
mFaceJointsScrollList = getChild<LLScrollListCtrl>("face_joints_scroll");
mFaceJointsScrollList->setCommitOnSelectionChange(true);
mFaceJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointSelect(); });
mFaceJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointTabSelect(); });
mHandJointsScrollList = getChild<LLScrollListCtrl>("hand_joints_scroll");
mHandJointsScrollList->setCommitOnSelectionChange(true);
mHandJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointSelect(); });
mHandJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointTabSelect(); });
mMiscJointsScrollList = getChild<LLScrollListCtrl>("misc_joints_scroll");
mMiscJointsScrollList->setCommitOnSelectionChange(true);
mMiscJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointSelect(); });
mMiscJointsScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointTabSelect(); });
mCollisionVolumesScrollList = getChild<LLScrollListCtrl>("collision_volumes_scroll");
mCollisionVolumesScrollList->setCommitOnSelectionChange(true);
mCollisionVolumesScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointSelect(); });
mCollisionVolumesScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onJointTabSelect(); });
mEntireAvJointScroll = getChild<LLScrollListCtrl>("entireAv_joint_scroll");
@ -197,8 +196,6 @@ bool FSFloaterPoser::postBuild()
mToggleDeltaModeBtn = getChild<LLButton>("delta_mode_toggle");
mRedoChangeBtn = getChild<LLButton>("button_redo_change");
mSetToTposeButton = getChild<LLButton>("set_t_pose_button");
mRecaptureJointsButton = getChild<LLButton>("button_RecaptureParts");
mRecaptureJointsButton->setEnabled(!sDisableRecaptureUntilStopPosing);
mJointsParentPnl = getChild<LLPanel>("joints_parent_panel");
mAdvancedParentPnl = getChild<LLPanel>("advanced_parent_panel");
@ -218,7 +215,7 @@ void FSFloaterPoser::onOpen(const LLSD& key)
{
onAvatarsRefresh();
refreshJointScrollListMembers();
onJointSelect();
onJointTabSelect();
onOpenSetAdvancedPanel();
refreshPoseScroll(mHandPresetsScrollList, POSE_PRESETS_HANDS_SUBDIRECTORY);
startPosingSelf();
@ -346,27 +343,34 @@ 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 savingDiff = !mPoserAnimator.posingStartedFromZeroRotations(avatar);
bool savingDiff = !mPoserAnimator.allBaseRotationsAreZero(avatar);
LLSD record;
record["version"]["value"] = (S32)4;
record["version"]["value"] = (S32)5;
record["startFromTeePose"]["value"] = !savingDiff;
LLVector3 rotation, position, scale, zeroVector;
bool baseRotationIsZero;
for (const FSPoserAnimator::FSPoserJoint& pj : mPoserAnimator.PoserJoints)
{
std::string bone_name = pj.jointName();
if (!mPoserAnimator.tryGetJointSaveVectors(avatar, pj, &rotation, &position, &scale))
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
record[bone_name] = bone_name;
record[bone_name]["enabled"] = posingThisJoint;
if (!posingThisJoint)
continue;
if (!mPoserAnimator.tryGetJointSaveVectors(avatar, pj, &rotation, &position, &scale, &baseRotationIsZero))
continue;
bool jointRotPosScaleAllZero = rotation == zeroVector && position == zeroVector && scale == zeroVector;
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
if (savingDiff && (!posingThisJoint || jointRotPosScaleAllZero))
if (savingDiff && jointRotPosScaleAllZero)
continue;
record[bone_name] = bone_name;
record[bone_name]["enabled"] = posingThisJoint;
record[bone_name]["jointBaseRotationIsZero"] = baseRotationIsZero;
record[bone_name]["rotation"] = rotation.getValue();
record[bone_name]["position"] = position.getValue();
record[bone_name]["scale"] = scale.getValue();
@ -413,7 +417,7 @@ void FSFloaterPoser::onClickToggleSelectedBoneEnabled()
refreshRotationSliders();
refreshTrackpadCursor();
refreshTextHighlightingOnAllScrollLists();
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onClickFlipSelectedJoints()
@ -436,7 +440,6 @@ void FSFloaterPoser::onClickFlipSelectedJoints()
if (!currentlyPosingJoint)
continue;
// need to be posing opposite joint too, or don't flip
auto oppositeJoint = mPoserAnimator.getPoserJointByName(item->mirrorJointName());
if (oppositeJoint)
{
@ -477,9 +480,6 @@ void FSFloaterPoser::onClickFlipPose()
void FSFloaterPoser::onClickRecaptureSelectedBones()
{
if (sDisableRecaptureUntilStopPosing)
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
@ -500,9 +500,10 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
mPoserAnimator.recaptureJoint(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName()));
}
setSavePosesButtonText(true);
refreshRotationSliders();
refreshTrackpadCursor();
refreshTextHighlightingOnAllScrollLists();
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onClickBrowsePoseCache()
@ -516,10 +517,7 @@ void FSFloaterPoser::onClickBrowsePoseCache()
void FSFloaterPoser::onPoseJointsReset()
{
// This is a double-click function: it needs to run twice within some amount of time to complete.
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
@ -577,15 +575,22 @@ void FSFloaterPoser::onPoseMenuAction(const LLSD& param)
return;
loadPoseFromXml(avatar, poseName, loadType);
onJointTabSelect();
refreshJointScrollListMembers();
setSavePosesButtonText(mPoserAnimator.allBaseRotationsAreZero(avatar));
}
bool FSFloaterPoser::notDoubleClicked()
{
auto timeIntervalSinceLastExecution = std::chrono::system_clock::now() - mTimeLastExecutedDoubleClickMethod;
mTimeLastExecutedDoubleClickMethod = std::chrono::system_clock::now();
return timeIntervalSinceLastExecution > mDoubleClickInterval;
}
void FSFloaterPoser::onClickLoadLeftHandPose()
{
// This is a double-click function: it needs to run twice within some amount of time to complete.
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
onClickLoadHandPose(false);
@ -593,10 +598,7 @@ void FSFloaterPoser::onClickLoadLeftHandPose()
void FSFloaterPoser::onClickLoadRightHandPose()
{
// This is a double-click function: it needs to run twice within some amount of time to complete.
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
onClickLoadHandPose(true);
@ -656,7 +658,7 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
continue;
vec3.setValue(control_map["rotation"]);
mPoserAnimator.setJointRotation(avatar, poserJoint, vec3, NONE, SWAP_NOTHING, NEGATE_NOTHING);
mPoserAnimator.loadJointRotation(avatar, poserJoint, true, vec3);
}
}
}
@ -688,10 +690,7 @@ bool FSFloaterPoser::poseFileStartsFromTeePose(const std::string& poseFileName)
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)
{
@ -738,8 +737,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
LLVector3 vec3;
LLQuaternion quat;
bool enabled;
bool setJointBaseRotationToZero;
S32 version = 0;
bool startFromZeroRot = false;
bool startFromZeroRot = true;
infile.open(fullPath);
if (!infile.is_open())
@ -770,12 +770,6 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
if (version > 3)
loadPositionsAndScalesAsDeltas = true;
if (startFromZeroRot) // legacy saves will always start from T-Pose, for better or worse.
{
disableRecapture();
mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar);
}
for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr)
{
std::string const& name = itr->first;
@ -785,29 +779,34 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
if (!poserJoint)
continue;
if (loadRotations && control_map.has("rotation"))
if (control_map.has("enabled"))
{
vec3.setValue(control_map["rotation"]);
mPoserAnimator.loadJointRotation(avatar, poserJoint, vec3);
enabled = control_map["enabled"].asBoolean();
mPoserAnimator.setPosingAvatarJoint(avatar, *poserJoint, enabled);
}
if (control_map.has("jointBaseRotationIsZero"))
setJointBaseRotationToZero = control_map["jointBaseRotationIsZero"].asBoolean();
else
setJointBaseRotationToZero = startFromZeroRot;
if (loadPositions && control_map.has("position"))
{
vec3.setValue(control_map["position"]);
mPoserAnimator.loadJointPosition(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
}
if (loadRotations && control_map.has("rotation"))
{
vec3.setValue(control_map["rotation"]);
mPoserAnimator.loadJointRotation(avatar, poserJoint, setJointBaseRotationToZero, vec3);
}
if (loadScales && control_map.has("scale"))
{
vec3.setValue(control_map["scale"]);
mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
}
if (control_map.has("enabled"))
{
enabled = control_map["enabled"].asBoolean();
mPoserAnimator.setPosingAvatarJoint(avatar, *poserJoint, enabled);
}
}
}
}
@ -815,8 +814,6 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
{
LL_WARNS("Posing") << "Everything caught fire trying to load the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL;
}
onJointSelect();
}
void FSFloaterPoser::startPosingSelf()
@ -845,7 +842,6 @@ void FSFloaterPoser::stopPosingSelf()
mPoserAnimator.stopPosingAvatar(avatar);
onAvatarSelect();
reEnableRecaptureIfAllowed();
}
void FSFloaterPoser::onPoseStartStop()
@ -858,7 +854,6 @@ void FSFloaterPoser::onPoseStartStop()
if (arePosingSelected)
{
mPoserAnimator.stopPosingAvatar(avatar);
reEnableRecaptureIfAllowed();
}
else
{
@ -912,6 +907,7 @@ void FSFloaterPoser::poseControlsEnable(bool enable)
void FSFloaterPoser::refreshJointScrollListMembers()
{
mEntireAvJointScroll->clearRows();
mBodyJointsScrollList->clearRows();
mFaceJointsScrollList->clearRows();
mHandJointsScrollList->clearRows();
@ -974,16 +970,10 @@ void FSFloaterPoser::refreshJointScrollListMembers()
}
if (item)
{
item->setUserdata((void*) &*poserJoint_iter);
LLVOAvatar* avatar = getUiSelectedAvatar();
if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint_iter))
((LLScrollListText *) item->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else
((LLScrollListText *) item->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
}
}
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::addHeaderRowToScrollList(const std::string& jointName, LLScrollListCtrl* bodyJointsScrollList)
@ -1002,8 +992,8 @@ LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHead
return NULL;
std::string headerValue = "";
if (hasString("icon_category") && hasString("icon_bone"))
headerValue = isHeaderRow ? getString("icon_category") : getString("icon_bone");
if (isHeaderRow && hasString("icon_category"))
headerValue = getString("icon_category");
std::string jointValue = jointName;
std::string parameterName = (isHeaderRow ? XML_LIST_HEADER_STRING_PREFIX : XML_LIST_TITLE_STRING_PREFIX) + jointName;
@ -1151,25 +1141,21 @@ void FSFloaterPoser::onUndoLastScale()
void FSFloaterPoser::onSetAvatarToTpose()
{
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
disableRecapture();
setSavePosesButtonText(false);
mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar);
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onResetPosition()
{
// This is a double-click function: it needs to run twice within some amount of time to complete.
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
@ -1196,10 +1182,7 @@ void FSFloaterPoser::onResetPosition()
void FSFloaterPoser::onResetScale()
{
// This is a double-click function: it needs to run twice within some amount of time to complete.
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
@ -1346,7 +1329,7 @@ void FSFloaterPoser::onToggleAdvancedPanel()
return;
reshape(poserFloaterWidth, poserFloaterHeight);
onJointSelect();
onJointTabSelect();
}
std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJoints() const
@ -1415,17 +1398,13 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
E_BoneDeflectionStyles FSFloaterPoser::getUiSelectedBoneDeflectionStyle() const
{
if (mToggleMirrorRotationBtn->getValue().asBoolean())
{
return MIRROR;
}
if (mToggleSympatheticRotationBtn->getValue().asBoolean())
{
return SYMPATHETIC;
}
if (mToggleDeltaModeBtn->getValue().asBoolean())
{
return DELTAMODE;
}
return NONE;
}
@ -1554,7 +1533,6 @@ void FSFloaterPoser::onLimbTrackballChanged()
yaw *= trackPadSensitivity;
pitch *= trackPadSensitivity;
// if the trackpad is in 'infinite scroll' mode, it can produce normalized-values outside the range of the sliders; this wraps them to by the slider full-scale
yaw = unWrapScale(yaw) * NormalTrackpadRangeInRads;
pitch = unWrapScale(pitch) * NormalTrackpadRangeInRads;
roll = unWrapScale(roll) * NormalTrackpadRangeInRads;
@ -1734,18 +1712,33 @@ void FSFloaterPoser::setSelectedJointsRotation(F32 yawInRadians, F32 pitchInRadi
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
LLVector3 vec3 = LLVector3(yawInRadians, pitchInRadians, rollInRadians);
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
LLVector3 vec3 = LLVector3(yawInRadians, pitchInRadians, rollInRadians);
auto selectedJoints = getUiSelectedPoserJoints();
bool savingToExternal = getWhetherToResetBaseRotationOnEdit();
for (auto item : getUiSelectedPoserJoints())
for (auto item : selectedJoints)
{
bool currentlyPosingJoint = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (!currentlyPosingJoint)
continue;
auto oppositeJoint = mPoserAnimator.getPoserJointByName(item->mirrorJointName());
if (oppositeJoint)
{
bool oppositeJointAlsoSelectedOnUi =
std::find(selectedJoints.begin(), selectedJoints.end(), oppositeJoint) != selectedJoints.end();
if (oppositeJointAlsoSelectedOnUi && item->dontFlipOnMirror())
continue;
}
mPoserAnimator.setJointRotation(avatar, item, vec3, defl, getJointTranslation(item->jointName()),
getJointNegation(item->jointName()));
getJointNegation(item->jointName()), savingToExternal);
}
if (savingToExternal)
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
@ -1785,7 +1778,7 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const
return rotation;
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()),
getJointNegation(selectedJoints.front()->jointName()), TARGETROTATION);
getJointNegation(selectedJoints.front()->jointName()));
return rotation;
}
@ -1826,7 +1819,7 @@ LLVector3 FSFloaterPoser::getScaleOfFirstSelectedJoint() const
return scale;
}
void FSFloaterPoser::onJointSelect()
void FSFloaterPoser::onJointTabSelect()
{
refreshAvatarPositionSliders();
refreshRotationSliders();
@ -1903,8 +1896,9 @@ void FSFloaterPoser::onAvatarSelect()
mStartStopPosingBtn->setValue(arePosingSelected);
mSetToTposeButton->setEnabled(arePosingSelected);
poseControlsEnable(arePosingSelected);
refreshTextHighlightingOnAllScrollLists();
onJointSelect();
refreshTextHighlightingOnAvatarScrollList();
refreshTextHighlightingOnJointScrollLists();
onJointTabSelect();
setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName();
}
@ -2038,10 +2032,10 @@ void FSFloaterPoser::onAvatarsRefresh()
}
mAvatarSelectionScrollList->updateLayout();
refreshTextHighlightingOnAllScrollLists();
refreshTextHighlightingOnAvatarScrollList();
}
void FSFloaterPoser::refreshTextHighlightingOnAllScrollLists()
void FSFloaterPoser::refreshTextHighlightingOnAvatarScrollList()
{
for (auto listItem : mAvatarSelectionScrollList->getAllData())
{
@ -2057,8 +2051,12 @@ void FSFloaterPoser::refreshTextHighlightingOnAllScrollLists()
else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
}
}
void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists()
{
LLVOAvatar *avatar = getUiSelectedAvatar();
addBoldToScrollList(mBodyJointsScrollList, avatar);
addBoldToScrollList(mFaceJointsScrollList, avatar);
addBoldToScrollList(mHandJointsScrollList, avatar);
@ -2066,21 +2064,9 @@ void FSFloaterPoser::refreshTextHighlightingOnAllScrollLists()
addBoldToScrollList(mCollisionVolumesScrollList, avatar);
}
void FSFloaterPoser::disableRecapture()
void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff)
{
mRecaptureJointsButton->setEnabled(false);
mSavePosesBtn->setLabel("Save Pose");
sDisableRecaptureUntilStopPosing = true;
}
void FSFloaterPoser::reEnableRecaptureIfAllowed()
{
if (posingAnyoneOnScrollList())
return;
mRecaptureJointsButton->setEnabled(true);
mSavePosesBtn->setLabel("Save Diff");
sDisableRecaptureUntilStopPosing = false;
setAsSaveDiff ? mSavePosesBtn->setLabel("Save Diff") : mSavePosesBtn->setLabel("Save Pose");
}
bool FSFloaterPoser::posingAnyoneOnScrollList()
@ -2109,15 +2095,31 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava
if (!list)
return;
std::string iconValue = "";
bool considerExternalFormatSaving = getWhetherToResetBaseRotationOnEdit();
if (considerExternalFormatSaving && hasString("icon_rotation_is_own_work"))
iconValue = getString("icon_rotation_is_own_work");
for (auto listItem : list->getAllData())
{
FSPoserAnimator::FSPoserJoint *userData = static_cast<FSPoserAnimator::FSPoserJoint *>(listItem->getUserdata());
if (userData)
if (!userData)
continue;
if (considerExternalFormatSaving)
{
if (mPoserAnimator.isPosingAvatarJoint(avatar, *userData))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
if (mPoserAnimator.baseRotationIsZero(avatar, *userData))
((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue(iconValue);
else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue("");
}
if (mPoserAnimator.isPosingAvatarJoint(avatar, *userData))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
}
}
bool FSFloaterPoser::getWhetherToResetBaseRotationOnEdit() { return gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); }

View File

@ -81,8 +81,6 @@ class FSFloaterPoser : public LLFloater
void onOpen(const LLSD& key) override;
void onClose(bool app_quitting) override;
static bool sDisableRecaptureUntilStopPosing;
/// <summary>
/// Refreshes the supplied pose list from the supplued subdirectory.
/// </summary>
@ -209,7 +207,7 @@ class FSFloaterPoser : public LLFloater
// UI Event Handlers:
void onAvatarsRefresh();
void onAvatarSelect();
void onJointSelect();
void onJointTabSelect();
void onToggleAdvancedPanel();
void onToggleMirrorChange();
void onToggleSympatheticChange();
@ -294,19 +292,20 @@ class FSFloaterPoser : public LLFloater
S32 getJointNegation(const std::string& jointName) const;
/// <summary>
/// Refreshes the text on all scroll lists based on their state.
/// Refreshes the text on the avatars scroll list based on their state.
/// </summary>
void refreshTextHighlightingOnAllScrollLists();
void refreshTextHighlightingOnAvatarScrollList();
/// <summary>
/// Disables recapturing joint traits.
/// Refreshes the text on all joints scroll lists based on their state.
/// </summary>
void disableRecapture();
void refreshTextHighlightingOnJointScrollLists();
/// <summary>
/// Recapture is be disabled if user is making their own pose (starting from a T-Pose).
/// Sets the text of the save pose button.
/// </summary>
void reEnableRecaptureIfAllowed();
/// <param name="setAsSaveDiff">Whether to indicate a diff will be saved, instead of a pose.</param>
void setSavePosesButtonText(bool setAsSaveDiff);
/// <summary>
/// Gets whether any avatar know by the UI is being posed.
@ -320,14 +319,28 @@ class FSFloaterPoser : public LLFloater
/// <param name="avatar">The avatar to whom the list is relevant.</param>
void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar);
/// <summary>
/// Determines if the user has run this method twice within mDoubleClickInterval.
/// </summary>
/// <returns>true if this method has executed since mDoubleClickInterval seconds ago, otherwise false.</returns>
bool notDoubleClicked();
/// <summary>
/// Gets whether the user wishes to reset the base-rotation to zero when they start editing a joint.
/// </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();
/// <summary>
/// The time when the last click of a button was made.
/// Utilized for controls needing a 'double click do' function.
/// </summary>
std::chrono::system_clock::time_point mTimeLastClickedJointReset = std::chrono::system_clock::now();
std::chrono::system_clock::time_point mTimeLastExecutedDoubleClickMethod = std::chrono::system_clock::now();
/// <summary>
/// The constant time interval, in seconds, a user must click twice within to successfully double-click a button.
/// The constant time interval, in seconds, a user must execute the notDoubleClicked twice to successfully 'double-click' a button.
/// </summary>
std::chrono::duration<double> const mDoubleClickInterval = std::chrono::duration<double>(0.3);
@ -336,6 +349,10 @@ class FSFloaterPoser : public LLFloater
/// </summary>
/// <param name="scale">The scale value from the trackball.</param>
/// <returns>A value appropriate for fitting a slider.</returns>
/// <remarks>
/// If the trackpad is in 'infinite scroll' mode, it can produce normalized-values outside the range of the sliders.
/// This method ensures whatever value the trackpad produces, they work with the sliders.
/// </remarks>
static F32 unWrapScale(F32 scale);
FSVirtualTrackpad* mAvatarTrackball{ nullptr };
@ -382,7 +399,6 @@ class FSFloaterPoser : public LLFloater
LLButton* mToggleDeltaModeBtn{ nullptr };
LLButton* mRedoChangeBtn{ nullptr };
LLButton* mSetToTposeButton{ nullptr };
LLButton* mRecaptureJointsButton{ nullptr };
LLLineEditor* mPoseSaveNameEditor{ nullptr };

View File

@ -0,0 +1,232 @@
/**
* @file fsjointpose.cpp
* @brief Container for the pose of a joint.
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (c) 2024 Angeldark Raymaker @ Second Life
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include <deque>
#include <boost/algorithm/string.hpp>
#include "fsposingmotion.h"
#include "llcharacter.h"
constexpr size_t MaximumUndoQueueLength = 20;
/// <summary>
/// The constant time interval, in seconds, specifying whether an 'undo' value should be added.
/// </summary>
constexpr std::chrono::duration<double> UndoUpdateInterval = std::chrono::duration<double>(0.3);
FSJointPose::FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume)
{
mJointState = new LLJointState;
mJointState->setJoint(joint);
mJointState->setUsage(usage);
mJointName = joint->getName();
mIsCollisionVolume = isCollisionVolume;
mRotation = FSJointRotation(joint->getRotation());
mBeginningPosition = joint->getPosition();
mBeginningScale = joint->getScale();
}
void FSJointPose::setPositionDelta(const LLVector3& pos)
{
addToUndo(mPositionDelta, &mUndonePositionIndex, &mLastSetPositionDeltas, &mTimeLastUpdatedPosition);
mPositionDelta.set(pos);
}
void FSJointPose::setRotationDelta(const LLQuaternion& rot)
{
addToUndo(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas, &mTimeLastUpdatedRotation);
mRotation = FSJointRotation(mRotation.baseRotation, rot);
}
void FSJointPose::setScaleDelta(const LLVector3& scale)
{
addToUndo(mScaleDelta, &mUndoneScaleIndex, &mLastSetScaleDeltas, &mTimeLastUpdatedScale);
mScaleDelta.set(scale);
}
void FSJointPose::undoLastPositionChange()
{
mPositionDelta.set(undoLastChange(mPositionDelta, &mUndonePositionIndex, &mLastSetPositionDeltas));
}
void FSJointPose::undoLastRotationChange()
{
mRotation.set(undoLastChange(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas));
}
void FSJointPose::undoLastScaleChange() { mScaleDelta.set(undoLastChange(mScaleDelta, &mUndoneScaleIndex, &mLastSetScaleDeltas)); }
void FSJointPose::redoLastPositionChange()
{
mPositionDelta.set(redoLastChange(mPositionDelta, &mUndonePositionIndex, &mLastSetPositionDeltas));
}
void FSJointPose::redoLastRotationChange()
{
mRotation.set(redoLastChange(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas));
}
void FSJointPose::redoLastScaleChange() { mScaleDelta.set(redoLastChange(mScaleDelta, &mUndoneScaleIndex, &mLastSetScaleDeltas)); }
template <typename T>
inline void FSJointPose::addToUndo(T delta, size_t* undoIndex, std::deque<T>* dequeue,
std::chrono::system_clock::time_point* timeLastUpdated)
{
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - *timeLastUpdated;
*timeLastUpdated = std::chrono::system_clock::now();
if (timeIntervalSinceLastChange < UndoUpdateInterval)
return;
if (*undoIndex > 0)
{
for (size_t i = 0; i < *undoIndex; i++)
dequeue->pop_front();
*undoIndex = 0;
}
dequeue->push_front(delta);
while (dequeue->size() > MaximumUndoQueueLength)
dequeue->pop_back();
}
template <typename T> T FSJointPose::undoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue)
{
if (dequeue->empty())
return thingToSet;
if (*undoIndex == 0)
dequeue->push_front(thingToSet);
*undoIndex += 1;
*undoIndex = llclamp(*undoIndex, 0, dequeue->size() - 1);
return dequeue->at(*undoIndex);
}
template <typename T> T FSJointPose::redoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue)
{
if (dequeue->empty())
return thingToSet;
if (*undoIndex == 0)
return thingToSet;
*undoIndex -= 1;
*undoIndex = llclamp(*undoIndex, 0, dequeue->size() - 1);
T result = dequeue->at(*undoIndex);
if (*undoIndex == 0)
dequeue->pop_front();
return result;
}
void FSJointPose::recaptureJoint()
{
if (mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
addToUndo(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas, &mTimeLastUpdatedRotation);
mRotation = FSJointRotation(joint->getRotation());
}
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
{
if (!oppositeJoint)
return;
if (mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
auto tempRot = FSJointRotation(mRotation);
mRotation = FSJointRotation(oppositeJoint->mRotation);
oppositeJoint->mRotation = tempRot;
}
void FSJointPose::revertJointScale()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setScale(mBeginningScale);
}
void FSJointPose::revertJointPosition()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setPosition(mBeginningPosition);
}
void FSJointPose::revertCollisionVolume()
{
if (!mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setRotation(mRotation.baseRotation);
joint->setPosition(mBeginningPosition);
joint->setScale(mBeginningScale);
}
void FSJointPose::reflectRotation()
{
if (mIsCollisionVolume)
return;
mRotation.reflectRotation();
}
void FSJointPose::zeroBaseRotation()
{
if (mIsCollisionVolume)
return;
mRotation.baseRotation = LLQuaternion::DEFAULT;
}
bool FSJointPose::isBaseRotationZero() const
{
if (mIsCollisionVolume)
return true;
return mRotation.baseRotation == LLQuaternion::DEFAULT;
}

254
indra/newview/fsjointpose.h Normal file
View File

@ -0,0 +1,254 @@
/**
* @file fsjointpose.h
* @brief Container for the pose of a joint.
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (c) 2024 Angeldark Raymaker @ Second Life
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef FS_JOINTPPOSE_H
#define FS_JOINTPPOSE_H
//-----------------------------------------------------------------------------
// Header files
//-----------------------------------------------------------------------------
#include "llmotion.h"
//-----------------------------------------------------------------------------
// class FSJointPose
//-----------------------------------------------------------------------------
class FSJointPose
{
public:
/// <summary>
/// A class encapsulating the positions/rotations for a joint.
/// </summary>
/// <param name="joint">The joint this joint pose represents.</param>
/// <param name="usage">The default usage the joint should have in a pose.</param>
/// <param name="isCollisionVolume">Whether the supplied joint is a collision volume.</param>
FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume = false);
/// <summary>
/// Gets the name of the joint.
/// </summary>
std::string jointName() const { return mJointName; }
/// <summary>
/// Gets whether this represents a collision volume.
/// </summary>
/// <returns>true if the joint is a collision volume, otherwise false.</returns>
bool isCollisionVolume() const { return mIsCollisionVolume; }
/// <summary>
/// Gets the position change the animator wishes the joint to have.
/// </summary>
LLVector3 getPositionDelta() const { return mPositionDelta; }
/// <summary>
/// Sets the position the animator wishes the joint to be in.
/// </summary>
void setPositionDelta(const LLVector3& pos);
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void undoLastPositionChange();
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void redoLastPositionChange();
/// <summary>
/// Gets the rotation the animator wishes the joint to be in.
/// </summary>
LLQuaternion getRotationDelta() const { return mRotation.deltaRotation; }
/// <summary>
/// Sets the rotation the animator wishes the joint to be in.
/// </summary>
void setRotationDelta(const LLQuaternion& rot);
/// <summary>
/// Reflects the base and delta rotation of the represented joint left-right.
/// </summary>
void reflectRotation();
/// <summary>
/// Sets the base rotation of the represented joint to zero.
/// </summary>
void zeroBaseRotation();
/// <summary>
/// Queries whether the represented joint is zero.
/// </summary>
/// <returns>True if the represented joint is zero, otherwise false.</returns>
bool isBaseRotationZero() const;
/// <summary>
/// Undoes the last rotation set, if any.
/// Ordinarily the queue does not contain the current rotation, because we rely on time to add, and not button-up.
/// When we undo, if we are at the top of the queue, we need to add the current rotation so we can redo back to it.
/// Thus when we start undoing, mUndoneRotationIndex points at the current rotation.
/// </summary>
void undoLastRotationChange();
/// <summary>
/// Redoes the last rotation set, if any.
/// </summary>
void redoLastRotationChange();
/// <summary>
/// Gets whether a redo of this joints rotation may be performed.
/// </summary>
/// <returns>true if the joint can have a redo applied, otherwise false.</returns>
bool canRedoRotation() const { return mUndoneRotationIndex > 0; }
/// <summary>
/// Gets the scale the animator wishes the joint to have.
/// </summary>
LLVector3 getScaleDelta() const { return mScaleDelta; }
/// <summary>
/// Sets the scale the animator wishes the joint to have.
/// </summary>
void setScaleDelta(const LLVector3& scale);
/// <summary>
/// Undoes the last scale set, if any.
/// </summary>
void undoLastScaleChange();
/// <summary>
/// Redoes the last scale set, if any.
/// </summary>
void redoLastScaleChange();
/// <summary>
/// Exchanges the rotations between two joints.
/// </summary>
void swapRotationWith(FSJointPose* oppositeJoint);
/// <summary>
/// Resets the beginning properties of the joint this represents.
/// </summary>
void recaptureJoint();
/// <summary>
/// Restores the joint represented by this to the scale it had when this motion started.
/// </summary>
void revertJointScale();
/// <summary>
/// Restores the joint represented by this to the position it had when this motion started.
/// </summary>
void revertJointPosition();
/// <summary>
/// Collision Volumes do not 'reset' their position/rotation when the animation stops.
/// This requires special treatment to revert changes we've made this animation session.
/// </summary>
void revertCollisionVolume();
LLVector3 getTargetPosition() const { return mPositionDelta + mBeginningPosition; }
LLQuaternion getTargetRotation() const { return mRotation.getTargetRotation(); }
LLVector3 getTargetScale() const { return mScaleDelta + mBeginningScale; }
/// <summary>
/// Gets the pointer to the jointstate for the joint this represents.
/// </summary>
LLPointer<LLJointState> getJointState() const { return mJointState; }
/// <summary>
/// A class wrapping base and delta rotation, attempting to keep baseRotation as secret as possible.
/// Among other things, facilitates easy undo/redo through the joint-recapture process.
/// </summary>
class FSJointRotation
{
public:
FSJointRotation(LLQuaternion base) { baseRotation.set(base); }
FSJointRotation(LLQuaternion base, LLQuaternion delta)
{
baseRotation.set(base);
deltaRotation.set(delta);
}
FSJointRotation() = default;
LLQuaternion baseRotation;
LLQuaternion deltaRotation;
LLQuaternion getTargetRotation() const { return deltaRotation * baseRotation; }
void reflectRotation()
{
baseRotation.mQ[VX] *= -1;
baseRotation.mQ[VZ] *= -1;
deltaRotation.mQ[VX] *= -1;
deltaRotation.mQ[VZ] *= -1;
}
void set(const FSJointRotation& jRot)
{
baseRotation.set(jRot.baseRotation);
deltaRotation.set(jRot.deltaRotation);
}
};
private:
std::string mJointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation.
LLPointer<LLJointState> mJointState{ nullptr };
/// <summary>
/// Collision Volumes require special treatment when we stop animating an avatar, as they do not revert to their original state
/// natively.
/// </summary>
bool mIsCollisionVolume{ false };
FSJointRotation mRotation;
std::deque<FSJointRotation> mLastSetRotationDeltas;
size_t mUndoneRotationIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedRotation = std::chrono::system_clock::now();
LLVector3 mPositionDelta;
LLVector3 mBeginningPosition;
std::deque<LLVector3> mLastSetPositionDeltas;
size_t mUndonePositionIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedPosition = std::chrono::system_clock::now();
/// <summary>
/// Joint scales require special treatment, as they do not revert when we stop animating an avatar.
/// </summary>
LLVector3 mScaleDelta;
LLVector3 mBeginningScale;
std::deque<LLVector3> mLastSetScaleDeltas;
size_t mUndoneScaleIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedScale = std::chrono::system_clock::now();
template <typename T>
void addToUndo(T delta, size_t* undoIndex, std::deque<T>* dequeue, std::chrono::system_clock::time_point* timeLastUpdated);
template <typename T> T undoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue);
template <typename T> T redoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue);
};
#endif // FS_JOINTPPOSE_H

View File

@ -45,7 +45,7 @@ bool FSPoserAnimator::isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint
if (posingMotion->isStopped())
return false;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
@ -68,7 +68,7 @@ void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoin
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
@ -90,12 +90,12 @@ void FSPoserAnimator::resetAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& j
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setTargetPosition(jointPose->getBeginningPosition());
jointPose->setTargetRotation(jointPose->getBeginningRotation());
jointPose->setPositionDelta(LLVector3());
jointPose->setRotationDelta(LLQuaternion());
}
void FSPoserAnimator::undoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -110,20 +110,20 @@ void FSPoserAnimator::undoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->undoLastRotationSet();
jointPose->undoLastRotationChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastRotationSet();
oppositeJointPose->undoLastRotationChange();
}
void FSPoserAnimator::undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -138,20 +138,20 @@ void FSPoserAnimator::undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->undoLastPositionSet();
jointPose->undoLastPositionChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastPositionSet();
oppositeJointPose->undoLastPositionChange();
}
void FSPoserAnimator::undoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -166,20 +166,20 @@ void FSPoserAnimator::undoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint&
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->undoLastScaleSet();
jointPose->undoLastScaleChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastScaleSet();
oppositeJointPose->undoLastScaleChange();
}
void FSPoserAnimator::resetJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -194,20 +194,20 @@ void FSPoserAnimator::resetJointPosition(LLVOAvatar* avatar, const FSPoserJoint&
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setTargetPosition(jointPose->getBeginningPosition());
jointPose->setPositionDelta(LLVector3());
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->setTargetPosition(oppositeJointPose->getBeginningPosition());
oppositeJointPose->setPositionDelta(LLVector3());
}
void FSPoserAnimator::resetJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -222,20 +222,20 @@ void FSPoserAnimator::resetJointScale(LLVOAvatar* avatar, const FSPoserJoint& jo
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setTargetScale(jointPose->getBeginningScale());
jointPose->setScaleDelta(LLVector3());
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->setTargetScale(oppositeJointPose->getBeginningScale());
oppositeJointPose->setScaleDelta(LLVector3());
}
bool FSPoserAnimator::canRedoJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint)
@ -250,7 +250,7 @@ bool FSPoserAnimator::canRedoJointRotation(LLVOAvatar* avatar, const FSPoserJoin
if (posingMotion->isStopped())
return false;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
@ -269,20 +269,20 @@ void FSPoserAnimator::redoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->redoLastRotationSet();
jointPose->redoLastRotationChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastRotationSet();
oppositeJointPose->redoLastRotationChange();
}
void FSPoserAnimator::redoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -297,20 +297,20 @@ void FSPoserAnimator::redoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->redoLastPositionSet();
jointPose->redoLastPositionChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastPositionSet();
oppositeJointPose->redoLastPositionChange();
}
void FSPoserAnimator::redoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -325,23 +325,23 @@ void FSPoserAnimator::redoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint&
if (posingMotion->isStopped())
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->redoLastScaleSet();
jointPose->redoLastScaleChange();
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastScaleSet();
oppositeJointPose->redoLastScaleChange();
}
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, bool forRecapture) const
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
LLVector3 pos;
if (!isAvatarSafeToUse(avatar))
@ -351,16 +351,11 @@ LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (!posingMotion)
return pos;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return pos;
if (forRecapture)
pos = jointPose->getCurrentPosition();
else
pos = jointPose->getTargetPosition();
return pos;
return jointPose->getPositionDelta();
}
void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style)
@ -378,33 +373,33 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(jn);
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(jn);
if (!jointPose)
return;
if (style == NONE)
{
jointPose->setTargetPosition(position);
jointPose->setPositionDelta(position);
return;
}
LLVector3 positionDelta = jointPose->getTargetPosition() - position;
LLVector3 positionDelta = jointPose->getPositionDelta() - position;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
LLVector3 oppositeJointPosition = oppositeJointPose->getTargetPosition();
LLVector3 oppositeJointPosition = oppositeJointPose->getPositionDelta();
switch (style)
{
case SYMPATHETIC:
jointPose->setTargetPosition(position);
oppositeJointPose->setTargetPosition(oppositeJointPosition - positionDelta);
jointPose->setPositionDelta(position);
oppositeJointPose->setPositionDelta(oppositeJointPosition - positionDelta);
break;
case MIRROR:
jointPose->setTargetPosition(position);
oppositeJointPose->setTargetPosition(oppositeJointPosition + positionDelta);
jointPose->setPositionDelta(position);
oppositeJointPose->setPositionDelta(oppositeJointPosition + positionDelta);
break;
default:
@ -412,7 +407,23 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
}
}
bool FSPoserAnimator::posingStartedFromZeroRotations(LLVOAvatar* avatar) const
bool FSPoserAnimator::baseRotationIsZero(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->isBaseRotationZero();
}
bool FSPoserAnimator::allBaseRotationsAreZero(LLVOAvatar* avatar) const
{
if (!isAvatarSafeToUse(avatar))
return false;
@ -441,40 +452,23 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
}
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());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation);
jointPose->setJointStartRotations(rot_quat);
jointPose->recaptureJoint();
setPosingAvatarJoint(avatar, joint, true);
}
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const
{
LLVector3 vec3;
if (!isAvatarSafeToUse(avatar))
@ -484,28 +478,15 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!posingMotion)
return vec3;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return vec3;
LLQuaternion rot;
switch (rotType)
{
case TARGETROTATION:
rot = jointPose->getTargetRotation();
break;
case CURRENTROTATION:
default:
rot = jointPose->getCurrentRotation();
break;
}
return translateRotationFromQuaternion(translation, negation, rot);
return translateRotationFromQuaternion(translation, negation, jointPose->getRotationDelta());
}
void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style,
E_BoneAxisTranslation translation, S32 negation)
void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style, E_BoneAxisTranslation translation, S32 negation,
bool resetBaseRotationToZero)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -516,42 +497,52 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (resetBaseRotationToZero)
jointPose->zeroBaseRotation();
LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation);
switch (style)
{
case SYMPATHETIC:
case MIRROR:
jointPose->setTargetRotation(rot_quat);
jointPose->setRotationDelta(rot_quat);
break;
case DELTAMODE:
jointPose->applyDeltaRotation(rot_quat);
return;
jointPose->setRotationDelta(rot_quat * jointPose->getRotationDelta());
break;
case NONE:
default:
jointPose->setTargetRotation(rot_quat);
jointPose->setRotationDelta(rot_quat);
return;
}
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
if (resetBaseRotationToZero)
oppositeJointPose->zeroBaseRotation();
LLQuaternion inv_quat;
switch (style)
{
case SYMPATHETIC:
oppositeJointPose->setTargetRotation(rot_quat);
oppositeJointPose->setRotationDelta(rot_quat);
break;
case MIRROR:
inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
oppositeJointPose->setTargetRotation(inv_quat);
oppositeJointPose->setRotationDelta(inv_quat);
break;
case DELTAMODE:
oppositeJointPose->setRotationDelta(rot_quat * oppositeJointPose->getRotationDelta());
break;
default:
@ -571,26 +562,18 @@ void FSPoserAnimator::reflectJoint(LLVOAvatar* avatar, const FSPoserJoint* joint
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
jointPose->reflectRotation();
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (oppositeJointPose)
{
LLQuaternion rot_quat = jointPose->getTargetRotation();
LLQuaternion inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
jointPose->setTargetRotation(inv_quat);
return;
oppositeJointPose->reflectRotation();
jointPose->swapRotationWith(oppositeJointPose);
}
LLQuaternion first_quat = jointPose->getTargetRotation();
LLQuaternion first_inv = LLQuaternion(-first_quat.mQ[VX], first_quat.mQ[VY], -first_quat.mQ[VZ], first_quat.mQ[VW]);
LLQuaternion second_quat = oppositeJointPose->getTargetRotation();
LLQuaternion second_inv = LLQuaternion(-second_quat.mQ[VX], second_quat.mQ[VY], -second_quat.mQ[VZ], second_quat.mQ[VW]);
jointPose->setTargetRotation(second_inv);
oppositeJointPose->setTargetRotation(first_inv);
}
void FSPoserAnimator::flipEntirePose(LLVOAvatar* avatar)
@ -725,7 +708,7 @@ LLVector3 FSPoserAnimator::translateRotationFromQuaternion(E_BoneAxisTranslation
return vec3;
}
LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, bool forRecapture) const
LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
LLVector3 scale;
if (!isAvatarSafeToUse(avatar))
@ -735,16 +718,11 @@ LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint&
if (!posingMotion)
return scale;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return scale;
if (forRecapture)
scale = jointPose->getCurrentScale();
else
scale = jointPose->getTargetScale();
return scale;
return jointPose->getScaleDelta();
}
void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style)
@ -762,23 +740,24 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(jn);
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(jn);
if (!jointPose)
return;
jointPose->setTargetScale(scale);
jointPose->setScaleDelta(scale);
if (style == NONE)
return;
FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->setTargetScale(scale);
oppositeJointPose->setScaleDelta(scale);
}
bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale)
bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos,
LLVector3* scale, bool* baseRotationIsZero)
{
if (!rot || !pos || !scale)
return false;
@ -790,19 +769,20 @@ bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJo
if (!posingMotion)
return false;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
LLQuaternion difference = jointPose->getTargetRotation() * jointPose->getBeginningRotation().conjugate(); // diff * q1 = q2 -> diff = q2 * inverse(q1)
LLQuaternion rotationDelta = jointPose->getRotationDelta();
rotationDelta.getEulerAngles(&rot->mV[VX], &rot->mV[VY], &rot->mV[VZ]);
pos->set(jointPose->getPositionDelta());
scale->set(jointPose->getScaleDelta());
*baseRotationIsZero = jointPose->isBaseRotationZero();
difference.getEulerAngles(&rot->mV[VX], &rot->mV[VY], &rot->mV[VZ]);
pos->set(jointPose->getTargetPosition() - jointPose->getBeginningPosition());
scale->set(jointPose->getTargetScale() - jointPose->getBeginningScale());
return true;
}
void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation)
void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, bool setBaseToZero, LLVector3 rotation)
{
if (!isAvatarSafeToUse(avatar) || !joint)
return;
@ -811,12 +791,15 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (setBaseToZero)
jointPose->zeroBaseRotation();
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setTargetRotation(rot * jointPose->getBeginningRotation());
jointPose->setRotationDelta(rot);
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
@ -828,14 +811,14 @@ void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint*
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (loadPositionAsDelta)
jointPose->setTargetPosition(jointPose->getBeginningPosition() + position);
jointPose->setPositionDelta(position);
else
jointPose->setTargetPosition(position);
jointPose->setPositionDelta(position);
}
void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale)
@ -847,14 +830,14 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (loadScaleAsDelta)
jointPose->setTargetScale(jointPose->getTargetScale() + scale);
jointPose->setScaleDelta(scale);
else
jointPose->setTargetScale(scale);
jointPose->setScaleDelta(scale);
}
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName)

View File

@ -56,15 +56,6 @@ typedef enum E_BoneDeflectionStyles
DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current
} E_BoneDeflectionStyles;
/// <summary>
/// When getting the rotation of a joint, we can apply different considerations to the rotation.
/// </summary>
typedef enum E_BoneRotationType
{
CURRENTROTATION = 0, // the current rotation the joint has
TARGETROTATION = 1, // the rotation the we want to achieve
} E_BoneRotationType;
/// <summary>
/// When we're going from bone-rotation to the UI sliders, some of the axes need swapping so they make sense in UI-terms.
/// eg: for one bone, the X-axis may mean up and down, but for another bone, the x-axis might be left-right.
@ -429,7 +420,7 @@ public:
/// <param name="avatar">The avatar whose joint is being queried.</param>
/// <param name="joint">The joint to determine the position for.</param>
/// <returns>The position of the requested joint, if determinable, otherwise a default vector.</returns>
LLVector3 getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, bool forRecapture = false) const;
LLVector3 getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Sets the position of a joint for the supplied avatar.
@ -449,7 +440,7 @@ public:
/// <param name="negation">The style of negation to dis-apply to the get.</param>
/// <param name="rotType">The type of rotation to get from the supplied joint for the supplied avatar.</param>
/// <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, E_BoneRotationType rotType) const;
LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const;
/// <summary>
/// Sets the rotation of a joint for the supplied avatar.
@ -460,8 +451,9 @@ public:
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
/// <param name="translation">The axial translation form the supplied joint.</param>
/// <param name="negation">The style of negation to apply to the set.</param>
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style,
E_BoneAxisTranslation translation, S32 negation);
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero);
/// <summary>
/// Gets the scale of a joint for the supplied avatar.
@ -469,7 +461,7 @@ public:
/// <param name="avatar">The avatar whose joint is being queried.</param>
/// <param name="joint">The joint to determine the scale for.</param>
/// <returns>The scale of the requested joint, if determinable, otherwise a default vector.</returns>
LLVector3 getJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, bool forRecapture = false) const;
LLVector3 getJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Sets the scale of a joint for the supplied avatar.
@ -517,7 +509,18 @@ public:
/// A save of the rotation 'deltas' facilitates a user saving their changes to an existing animation.
/// Thus the save represents 'nothing other than the changes the user made', to some other pose which they may have limited rights to.
/// </remarks>
bool posingStartedFromZeroRotations(LLVOAvatar* avatar) const;
bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
/// </summary>
/// <param name="avatar">The avatar whose pose-rotations are being considered for saving.</param>
/// <returns>True if the save should save only 'deltas' to the rotation, otherwise false.</returns>
/// <remarks>
/// A save of the rotation 'deltas' facilitates a user saving their changes to an existing animation.
/// Thus the save represents 'nothing other than the changes the user made', to some other pose which they may have limited rights to.
/// </remarks>
bool allBaseRotationsAreZero(LLVOAvatar* avatar) const;
/// <summary>
/// Tries to get the rotation, position and scale changes from initial conditions, to save in some export container.
@ -527,26 +530,28 @@ public:
/// <param name="rot">The quaternion to store the rotation to save in.</param>
/// <param name="pos">The vector to store the position to save in.</param>
/// <param name="scale">The vector to store the scale to save in.</param>
/// <param name="baseRotationIsZero">The bool to store whether the base rotation is zero.</param>
/// <returns>True if the joint should be saved, otherwise false.</returns>
/// <remarks>
/// Our objective is to protect peoples novel work: the poses created with this, and poses from other sources, such as in-world.
/// In all scenarios, this yeilds 'deltas' of rotation/position/scale.
/// The deltas represent the user's novel work, and may be relative to some initial values (as from a pose), or to 'nothing' (such as all rotations == 0, or, the 'T-Pose').
/// </remarks>
bool tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale);
bool tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale, bool* baseRotationIsZero);
/// <summary>
/// Loads a joint rotation for the supplied joint on the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to load the rotation for.</param>
/// <param name="joint">The joint to load the rotation for.</param>
/// <param name="setBaseToZero">Whether to start from a zero base rotation.</param>
/// <param name="rotation">The rotation to load.</param>
/// <remarks>
/// 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,
/// or whether its rotation is zero, the result is always the same: just 'add' the supplied rotation to the existing rotation.
/// </remarks>
void loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation);
void loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, bool setBaseToZero, LLVector3 rotation);
/// <summary>
/// Loads a joint position for the supplied joint on the supplied avatar.
@ -622,9 +627,6 @@ public:
/// <returns>True if the avatar is safe to manipulate, otherwise false.</returns>
bool isAvatarSafeToUse(LLVOAvatar* avatar) const;
void setStartingJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation,
E_BoneAxisTranslation translation, S32 negation);
/// <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.

View File

@ -48,7 +48,7 @@ LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character
if (!targetJoint)
continue;
FSJointPose jointPose = FSJointPose(targetJoint);
FSJointPose jointPose = FSJointPose(targetJoint, POSER_JOINT_STATE);
mJointPoses.push_back(jointPose);
addJointState(jointPose.getJointState());
@ -59,7 +59,7 @@ LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character
if (!targetJoint)
continue;
FSJointPose jointPose = FSJointPose(targetJoint, true);
FSJointPose jointPose = FSJointPose(targetJoint, POSER_JOINT_STATE, true);
mJointPoses.push_back(jointPose);
addJointState(jointPose.getJointState());
@ -91,9 +91,9 @@ bool FSPosingMotion::onUpdate(F32 time, U8* joint_mask)
currentRotation = joint->getRotation();
currentPosition = joint->getPosition();
currentScale = joint->getScale();
targetRotation = jointPose.getTargetRotation();
targetPosition = jointPose.getTargetPosition();
targetScale = jointPose.getTargetScale();
targetRotation = jointPose.getTargetRotation();
targetPosition = jointPose.getTargetPosition();
targetScale = jointPose.getTargetScale();
if (currentPosition != targetPosition)
{
@ -213,7 +213,7 @@ void FSPosingMotion::setJointState(LLJoint* joint, U32 state)
addJointState(jointPose->getJointState());
}
FSPosingMotion::FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
{
if (mJointPoses.size() < 1)
return nullptr;
@ -251,16 +251,12 @@ bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint)
bool FSPosingMotion::allStartingRotationsAreZero() const
{
LLQuaternion zeroQuat;
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
if (poserJoint_iter->jointName() == "mPelvis")
continue;
if (poserJoint_iter->isCollisionVolume())
continue;
LLQuaternion quat = poserJoint_iter->getBeginningRotation();
if (quat != zeroQuat)
if (!poserJoint_iter->isBaseRotationZero())
return false;
}
@ -269,14 +265,8 @@ 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->setJointStartRotations(zeroQuat);
}
poserJoint_iter->zeroBaseRotation();
}
constexpr size_t MaximumUndoQueueLength = 20;
@ -285,251 +275,3 @@ constexpr size_t MaximumUndoQueueLength = 20;
/// The constant time interval, in seconds, specifying whether an 'undo' value should be added.
/// </summary>
constexpr std::chrono::duration<double> UndoUpdateInterval = std::chrono::duration<double>(0.3);
void FSPosingMotion::FSJointPose::addLastPositionToUndo()
{
if (mUndonePositionIndex > 0)
{
for (int i = 0; i < mUndonePositionIndex; i++)
mLastSetPositions.pop_front();
mUndonePositionIndex = 0;
}
mLastSetPositions.push_front(mTargetPosition);
while (mLastSetPositions.size() > MaximumUndoQueueLength)
mLastSetPositions.pop_back();
}
void FSPosingMotion::FSJointPose::addLastRotationToUndo()
{
if (mUndoneRotationIndex > 0)
{
for (int i = 0; i < mUndoneRotationIndex; i++)
mLastSetRotations.pop_front();
mUndoneRotationIndex = 0;
}
mLastSetRotations.push_front(mTargetRotation);
while (mLastSetRotations.size() > MaximumUndoQueueLength)
mLastSetRotations.pop_back();
}
void FSPosingMotion::FSJointPose::addLastScaleToUndo()
{
if (mUndoneScaleIndex > 0)
{
for (int i = 0; i < mUndoneScaleIndex; i++)
mLastSetScales.pop_front();
mUndoneScaleIndex = 0;
}
mLastSetScales.push_front(mTargetScale);
while (mLastSetScales.size() > MaximumUndoQueueLength)
mLastSetScales.pop_back();
}
LLVector3 FSPosingMotion::FSJointPose::getCurrentPosition()
{
LLVector3 vec3;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return vec3;
vec3 = joint->getPosition();
return vec3;
}
void FSPosingMotion::FSJointPose::setTargetPosition(const LLVector3& pos)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedPosition;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastPositionToUndo();
mTimeLastUpdatedPosition = std::chrono::system_clock::now();
mTargetPosition.set(pos);
}
LLQuaternion FSPosingMotion::FSJointPose::getCurrentRotation()
{
LLQuaternion quat;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return quat;
quat = joint->getRotation();
return quat;
}
void FSPosingMotion::FSJointPose::setTargetRotation(const LLQuaternion& rot)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedRotation;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastRotationToUndo();
mTimeLastUpdatedRotation = std::chrono::system_clock::now();
mTargetRotation.set(rot);
}
void FSPosingMotion::FSJointPose::applyDeltaRotation(const LLQuaternion& rot)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedRotation;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastRotationToUndo();
mTimeLastUpdatedRotation = std::chrono::system_clock::now();
mTargetRotation = mTargetRotation * rot;
}
LLVector3 FSPosingMotion::FSJointPose::getCurrentScale()
{
LLVector3 vec3;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return vec3;
vec3 = joint->getScale();
return vec3;
}
void FSPosingMotion::FSJointPose::setTargetScale(LLVector3 scale)
{
auto timeIntervalSinceLastScaleChange = std::chrono::system_clock::now() - mTimeLastUpdatedScale;
if (timeIntervalSinceLastScaleChange > UndoUpdateInterval)
addLastScaleToUndo();
mTimeLastUpdatedScale = std::chrono::system_clock::now();
mTargetScale.set(scale);
}
void FSPosingMotion::FSJointPose::undoLastPositionSet()
{
if (mLastSetPositions.empty())
return;
if (mUndonePositionIndex == 0) // at the top of the queue add the current
addLastPositionToUndo();
mUndonePositionIndex++;
mUndonePositionIndex = llclamp(mUndonePositionIndex, 0, mLastSetPositions.size() - 1);
mTargetPosition.set(mLastSetPositions[mUndonePositionIndex]);
}
void FSPosingMotion::FSJointPose::redoLastPositionSet()
{
if (mLastSetPositions.empty())
return;
mUndonePositionIndex--;
mUndonePositionIndex = llclamp(mUndonePositionIndex, 0, mLastSetPositions.size() - 1);
mTargetPosition.set(mLastSetPositions[mUndonePositionIndex]);
if (mUndonePositionIndex == 0)
mLastSetPositions.pop_front();
}
void FSPosingMotion::FSJointPose::undoLastRotationSet()
{
if (mLastSetRotations.empty())
return;
if (mUndoneRotationIndex == 0) // at the top of the queue add the current
addLastRotationToUndo();
mUndoneRotationIndex++;
mUndoneRotationIndex = llclamp(mUndoneRotationIndex, 0, mLastSetRotations.size() - 1);
mTargetRotation.set(mLastSetRotations[mUndoneRotationIndex]);
}
void FSPosingMotion::FSJointPose::redoLastRotationSet()
{
if (mLastSetRotations.empty())
return;
mUndoneRotationIndex--;
mUndoneRotationIndex = llclamp(mUndoneRotationIndex, 0, mLastSetRotations.size() - 1);
mTargetRotation.set(mLastSetRotations[mUndoneRotationIndex]);
if (mUndoneRotationIndex == 0)
mLastSetRotations.pop_front();
}
void FSPosingMotion::FSJointPose::undoLastScaleSet()
{
if (mLastSetScales.empty())
return;
if (mUndoneScaleIndex == 0)
addLastScaleToUndo();
mUndoneScaleIndex++;
mUndoneScaleIndex = llclamp(mUndoneScaleIndex, 0, mLastSetScales.size() - 1);
mTargetScale.set(mLastSetScales[mUndoneScaleIndex]);
}
void FSPosingMotion::FSJointPose::redoLastScaleSet()
{
if (mLastSetScales.empty())
return;
mUndoneScaleIndex--;
mUndoneScaleIndex = llclamp(mUndoneScaleIndex, 0, mLastSetScales.size() - 1);
mTargetScale.set(mLastSetScales[mUndoneScaleIndex]);
if (mUndoneScaleIndex == 0)
mLastSetScales.pop_front();
}
void FSPosingMotion::FSJointPose::revertJointScale()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setScale(mBeginningScale);
}
void FSPosingMotion::FSJointPose::revertJointPosition()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setPosition(mBeginningPosition);
}
void FSPosingMotion::FSJointPose::revertCollisionVolume()
{
if (!mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setRotation(mBeginningRotation);
joint->setPosition(mBeginningPosition);
joint->setScale(mBeginningScale);
}
void FSPosingMotion::FSJointPose::setJointStartRotations(LLQuaternion quat) { mBeginningRotation = mTargetRotation = quat; }
FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume)
{
mJointState = new LLJointState;
mJointState->setJoint(joint);
mJointState->setUsage(POSER_JOINT_STATE);
mJointName = joint->getName();
mIsCollisionVolume = isCollisionVolume;
mBeginningRotation = mTargetRotation = joint->getRotation();
mBeginningPosition = mTargetPosition = joint->getPosition();
mBeginningScale = mTargetScale = joint->getScale();
}

View File

@ -1,5 +1,5 @@
/**
* @file fsposingmotion.cpp
* @file fsposingmotion.h
* @brief Model for posing your (and other) avatar(s).
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
@ -31,6 +31,7 @@
// Header files
//-----------------------------------------------------------------------------
#include "llmotion.h"
#include "fsjointpose.h"
#define MIN_REQUIRED_PIXEL_AREA_POSING 500.f
@ -47,195 +48,6 @@ public:
public:
static LLMotion *create(const LLUUID &id) { return new FSPosingMotion(id); }
/// <summary>
/// A class encapsulating the positions/rotations for a joint.
/// </summary>
class FSJointPose
{
std::string mJointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation.
LLPointer<LLJointState> mJointState{ nullptr };
/// <summary>
/// Collision Volumes require special treatment when we stop animating an avatar, as they do not revert to their original state natively.
/// </summary>
bool mIsCollisionVolume{ false };
LLQuaternion mTargetRotation;
LLQuaternion mBeginningRotation;
std::deque<LLQuaternion> mLastSetRotations;
size_t mUndoneRotationIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedRotation = std::chrono::system_clock::now();
LLVector3 mTargetPosition;
LLVector3 mBeginningPosition;
std::deque<LLVector3> mLastSetPositions;
size_t mUndonePositionIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedPosition = std::chrono::system_clock::now();
/// <summary>
/// Joint scales require special treatment, as they do not revert when we stop animating an avatar.
/// </summary>
LLVector3 mTargetScale;
LLVector3 mBeginningScale;
std::deque<LLVector3> mLastSetScales;
size_t mUndoneScaleIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedScale = std::chrono::system_clock::now();
/// <summary>
/// Adds a last position to the deque.
/// </summary>
void addLastPositionToUndo();
/// <summary>
/// Adds a last rotation to the deque.
/// </summary>
void addLastRotationToUndo();
/// <summary>
/// Adds a last rotation to the deque.
/// </summary>
void addLastScaleToUndo();
public:
/// <summary>
/// Gets the name of the joint.
/// </summary>
std::string jointName() const { return mJointName; }
/// <summary>
/// Gets whether this represents a collision volume.
/// </summary>
/// <returns></returns>
bool isCollisionVolume() const { return mIsCollisionVolume; }
/// <summary>
/// Gets whether a redo of this joints rotation may be performed.
/// </summary>
/// <returns></returns>
bool canRedoRotation() const { return mUndoneRotationIndex > 0; }
/// <summary>
/// Gets the position the joint was in when the animation was initialized.
/// </summary>
LLVector3 getBeginningPosition() const { return mBeginningPosition; }
/// <summary>
/// Gets the position the animator wishes the joint to be in.
/// </summary>
LLVector3 getTargetPosition() const { return mTargetPosition; }
/// <summary>
/// Gets the position the animator wishes the joint to be in.
/// </summary>
LLVector3 getCurrentPosition();
/// <summary>
/// Sets the position the animator wishes the joint to be in.
/// </summary>
void setTargetPosition(const LLVector3& pos);
/// <summary>
/// Gets the rotation the joint was in when the animation was initialized.
/// </summary>
LLQuaternion getBeginningRotation() const { return mBeginningRotation; }
/// <summary>
/// Gets the rotation the animator wishes the joint to be in.
/// </summary>
LLQuaternion getTargetRotation() const { return mTargetRotation; }
/// <summary>
/// Gets the rotation of the joint.
/// </summary>
LLQuaternion getCurrentRotation();
/// <summary>
/// Sets the rotation the animator wishes the joint to be in.
/// </summary>
void setTargetRotation(const LLQuaternion& rot);
/// <summary>
/// Applies a delta to the rotation the joint currently targets.
/// </summary>
void applyDeltaRotation(const LLQuaternion& rot);
/// <summary>
/// Gets the scale the animator wishes the joint to have.
/// </summary>
LLVector3 getTargetScale() const { return mTargetScale; }
/// <summary>
/// Gets the scale the joint has.
/// </summary>
LLVector3 getCurrentScale();
/// <summary>
/// Gets the scale the joint had when the animation was initialized.
/// </summary>
LLVector3 getBeginningScale() const { return mBeginningScale; }
/// <summary>
/// Sets the scale the animator wishes the joint to have.
/// </summary>
void setTargetScale(LLVector3 scale);
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void undoLastPositionSet();
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void redoLastPositionSet();
/// <summary>
/// Undoes the last rotation set, if any.
/// Ordinarily the queue does not contain the current rotation, because we rely on time to add, and not button-up.
/// When we undo, if we are at the top of the queue, we need to add the current rotation so we can redo back to it.
/// Thus when we start undoing, mUndoneRotationIndex points at the current rotation.
/// </summary>
void undoLastRotationSet();
/// <summary>
/// Undoes the last rotation set, if any.
/// </summary>
void redoLastRotationSet();
void undoLastScaleSet();
void redoLastScaleSet();
/// <summary>
/// Restores the joint represented by this to the scale it had when this motion started.
/// </summary>
void revertJointScale();
/// <summary>
/// Restores the joint represented by this to the position it had when this motion started.
/// </summary>
void revertJointPosition();
/// <summary>
/// Collision Volumes do not 'reset' their position/rotation when the animation stops.
/// This requires special treatment to revert changes we've made this animation session.
/// </summary>
void revertCollisionVolume();
/// <summary>
/// Sets the beginning and target rotations to the supplied rotation.
/// </summary>
void setJointStartRotations(LLQuaternion quat);
/// <summary>
/// Gets the pointer to the jointstate for the joint this represents.
/// </summary>
LLPointer<LLJointState> getJointState() const { return mJointState; }
FSJointPose(LLJoint* joint, bool isCollisionVolume = false);
};
public:
virtual bool getLoop() { return true; }
virtual F32 getDuration() { return 0.0; }
@ -323,10 +135,16 @@ private:
LLAssetID mMotionID;
/// <summary>
/// The amount of time, in seconds, we use for transitioning between one animation-state to another; this affects the 'fluidity'
/// of motion between changes to a joint.
/// The time constant, in seconds, we use for transitioning between one animation-state to another; this affects the 'damping'
/// of motion between changes to a joint. 'Constant' in this context is not a reference to the language-idea of 'const' value.
/// Smaller is less damping => faster transition.
/// As implemented, the actual rotation of a joint decays towards the target rotation in something akin to (if not) an exponential.
/// This time constant effects how fast this decay occurs rather than how long it takes to complete (it often never completes).
/// This contributes to the fact that actual rotation != target rotation (in addition to rotation being non-monotonic).
/// Use caution making this larger than the subjective amount of time between adjusting a joint and then choosing to use 'undo' it.
/// Undo-function waits a similar amount of time after the last user-incited joint change to add a 'restore point'.
/// Undo-function waits an amount of time after the last user-incited joint change to add a 'restore point'.
/// Important to note is that the actual rotation/position/scale never reaches the target, which seems absurd, however
/// it's the user that closes the feedback loop here: if they want more change, they input more until the result is as they like it.
/// </summary>
const F32 mInterpolationTime = 0.25f;
@ -366,4 +184,3 @@ private:
};
#endif // FS_POSINGMOTION_H

View File

@ -33,6 +33,8 @@
#include "lltrans.h"
#include "llviewertexturelist.h"
#include "rlvactions.h"
// ============================================================================
// Helper functions
@ -42,6 +44,11 @@ LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p)
{
const LLSD& sdTooltip = p.create_params;
// <FS:Ansariel> Texture tooltip RLVa fix
if (!RlvActions::canPreviewTextures())
return LLUICtrlFactory::create<LLToolTip>(p);
// </FS:Ansariel>
if (sdTooltip.has("thumbnail_id") && sdTooltip["thumbnail_id"].asUUID().notNull())
{
// go straight for thumbnail regardless of type

View File

@ -102,7 +102,7 @@
<check_box label="Anizotrop filtrləmə (aktiv olduqda yavaşladır)" name="ani" tool_tip = "Bu qutu anizotrop filtrləməni təmin edir. Bu, kamera mövqeyinə nisbətən nisbətən böyük bucaqlarla baxıldıqda teksturların keyfiyyətini artırmaq üçün bir texnikadır. Adətən teksturlar daha uzun məsafələrdə daha az bulanıq görünür."/>
<check_box label="Axın VBO-nu yandır" name="vbo_stream" tool_tip="Bunun söndürülməsi VBO aktivləşdirildikdə performansı yaxşılaşdıra bilər. Müşahidə göstərdi ki, söndürülməsi müxtəlif AMD və Intel qrafik kartlarında təkmilləşdirmələr təmin edib."/>
<check_box label="İtkili tekstura sıxılmasını aktivləşdirin (yenidən başlama tələb olunur)" name="texture compression" tool_tip="Video yaddaşda teksturanın sıxılması rəng keyfiyyətində müəyyən itkilər hesabına yüksək rezolyusiyaya malik teksturaların yüklənməsinə imkan verir."/>
<check_box label="HiDPI displey dəstəyini aktivləşdirin (yalnız Mac OSX; yenidən başladın tələb olunur)" name="use HiDPI" tool_tip="Yüksək nisbət ilə göstərmək üçün OpenGL-i aktiv edin."/>
<check_box label="HiDPI displey dəstəyini aktivləşdirin (yalnız macOS; yenidən başladın tələb olunur)" name="use HiDPI" tool_tip="Yüksək nisbət ilə göstərmək üçün OpenGL-i aktiv edin."/>
<text name="Antialiasing:" tool_tip = "Bu seçimin dəyişdirilməsi bəzi avadanlıqların yenidən başlamasını tələb edə bilər." width="290">
Hamarlaşdırma (yenidən başlatma tövsiyyə olunur):
</text>

View File

@ -222,6 +222,7 @@
</text>
<slider name="trackpad_sensitivity_slider" tool_tip="Stellt die Sensitivität der Steuerkugel ein"/>
<check_box name="stop_posing_on_close_checkbox" label="Beim Schließen stoppen" tool_tip="Die Pose beim Schließen nicht zu stoppen kann hilfreich sein, um die bisher getätigte Arbeit nicht zu verlieren."/>
<check_box name="reset_base_rotation_on_edit_checkbox" label="Basis-Rot. beim Edit. zurück." tool_tip="Wenn das erste Mal eine Pose bearbeitet wird, diese auf Null zurücksetzen. Hierdurch eine eine komplette Pose und nicht nur die Differenz gespeichert werden. Ein grüner Haken erscheint neben jedem Gelenk, dass auf Null-basierend exportiert wurde."/>
</panel>
</tab_container>
</panel>

View File

@ -143,7 +143,7 @@
S3TC aktivieren:
</text>
<check_box label="Textur-Komprimierung aktivieren (Erfordert Neustart)" name="texture compression" tool_tip="Komprimiert Texturen im Videospeicher und erlaubt dadurch eine höhrere Auflösung und/oder Laden von mehr Texturen auf Kosten der Textqualität."/>
<check_box label="Unterstützung für HiDPI-Displays aktivieren (nur Mac OSX; erfordert Neustart)" name="use HiDPI" tool_tip="Aktiviert OpenGL für High-Resolution Drawing."/>
<check_box label="Unterstützung für HiDPI-Displays aktivieren (nur macOS; erfordert Neustart)" name="use HiDPI" tool_tip="Aktiviert OpenGL für High-Resolution Drawing."/>
<text name="antialiasing label" tool_tip="Eine Änderung dieser Einstellung kann auf bestimmter Hardware einen Neustart erfordern.">
Antialiasing (Neustart empfohlen):
</text>

View File

@ -9,6 +9,7 @@ width="403">
<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_is_own_work" translate="false">Check_Mark</string>
<!-- Provides for axis-swapping per joint for slider/trackpad control -->
<!-- Begins with joint_transform_ then has an internal joint name, ONE swap choice of:
@ -799,6 +800,16 @@ width="403">
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="5"
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" />
</panel>
</tab_container>
</panel>
@ -1422,7 +1433,7 @@ width="403">
layout="topleft"
left="10"
max_val="2"
min_val="0"
min_val="-2"
name="Advanced_Scale_X"
top_pad="6"
width="380" >
@ -1441,7 +1452,7 @@ width="403">
layout="topleft"
left_delta="0"
max_val="2"
min_val="0"
min_val="-2"
name="Advanced_Scale_Y"
top_pad="1"
width="380" >
@ -1460,7 +1471,7 @@ width="403">
layout="topleft"
left_delta="0"
max_val="2"
min_val="0"
min_val="-2"
name="Advanced_Scale_Z"
top_pad="1"
width="380" >

View File

@ -976,7 +976,7 @@
control_name="RenderHiDPI"
height="16"
initial_value="true"
label="Enable support for HiDPI displays (Mac OSX only; requires restart)"
label="Enable support for HiDPI displays (macOS only; requires restart)"
layout="topleft"
left="10"
top_pad="5"

View File

@ -104,7 +104,7 @@
<panel label="Paramètres matériels" name="Hardware Settings">
<check_box label="Filtrage Anisotrope (plus lent lorsqu'il est activé)" name="ani"/>
<check_box label="Activer la compression de textures sans pertes (Redémarrage requis)" name="texture compression" tool_tip="Activer la compression des textures dans la mémoire graphique permet de charger des textures de résolution supérieure et une prise en charge d'un nombre de textures plus élevé pour le même cout mémoire."/>
<check_box label="Active la prise en charge des écrans HiDPI (Mac OSX seulement; Redémarrage requis)" name="use HiDPI" tool_tip="Active OpenGL pour l'affichage haute résolution."/>
<check_box label="Active la prise en charge des écrans HiDPI (macOS seulement; Redémarrage requis)" name="use HiDPI" tool_tip="Active OpenGL pour l'affichage haute résolution."/>
<text width="235" name="Antialiasing:" tool_tip="La modification de ce paramètre peut nécessiter un redémarrage de certains ordinateurs.">
Anticrénelage (redémarrage recommandé) :
</text>

View File

@ -117,7 +117,7 @@
<panel label="Impostazioni Hardware" name="Hardware Settings">
<check_box label="Filtro Anisotropico (rallenta se attivo)" name="ani" tool_tip="Il filtro anisotropico è un metodo per migliorare la qualità delle texture quando vengono visualizzate ad angoli relativamente ampi rispetto alla posizione della fotocamera. Di solito le rende meno sfocate a distanze maggiori."/>
<check_box label="Abilita compressione texture (richiede riavvio)" name="texture compression" tool_tip="Le texture vengono compresse nella memoria video, questo consente per le texture con una risoluzione alta di essere caricate più rapidamente perdendo qualità nel colore."/>
<check_box label="Abilita supporto display HiDPI (solo Mac OSX; richiede riavvio)" name="use HiDPI" tool_tip="Abilita OpenGL per il disegno ad alta risoluzione." />
<check_box label="Abilita supporto display HiDPI (solo macOS; richiede riavvio)" name="use HiDPI" tool_tip="Abilita OpenGL per il disegno ad alta risoluzione." />
<text name="Antialiasing:" tool_tip="Le modifiche a questa impostazione potrebbero richiedere il riavvio su alcuni dispositivi.">
Antialiasing:
</text>

View File

@ -133,7 +133,7 @@
<check_box label="異方性フィルタリング(有効にすると速度が低下します)" name="ani" tool_tip="ここにチェックを入れると、異方性フィルタリングが有効になります。これは、自分のカメラの位置に対して、正面ではない角度で見えているテクスチャの品質を向上させるものです。通常、より遠くにある部分がぼやけて見えるのを防ぎます。" />
<check_box label="OpenGL Vertex Buffer Objectsを有効化" name="vbo" tool_tip="最新のハードウェアでこの設定を有効にすると、パフォーマンスが向上します。しかし、旧型のハードウェアではVBOの実装が貧弱な場合が多く、この設定を有効にすることでクラッシュにつながるおそれがあります。" />
<check_box label="テクスチャの不可逆圧縮(再起動が必要)" name="texture compression" tool_tip="ビデオメモリ内のテクスチャを圧縮することで、色の品質が低下する代わりに、より高解像度のテクスチャやより多くのテクスチャを読み込むことを可能にします。" />
<check_box label="HiDPI 表示対応を有効にする(Mac OSX のみ。再起動が必要。)" name="use HiDPI" tool_tip="高解像度描画のための OpenGL を有効にします" />
<check_box label="HiDPI 表示対応を有効にする(macOS のみ。再起動が必要。)" name="use HiDPI" tool_tip="高解像度描画のための OpenGL を有効にします" />
<text name="Antialiasing:" tool_tip="ハードウェアによってはこの設定の変更を適用するには再起動が必要な場合があります。" >
アンチエイリアス:
</text>

View File

@ -214,6 +214,7 @@
<text name="trackpad_sensitivity_label">Czułość trackpada:</text>
<slider name="trackpad_sensitivity_slider" tool_tip="Dostosowuje czułość trackballa / kółka" />
<check_box name="stop_posing_on_close_checkbox" label="Stop pozy po zamknięciu" tool_tip="Brak zatrzymywania pozy może być pomocny, jeśli nie chcesz jej przypadkowo stracić." />
<check_box name="reset_base_rotation_on_edit_checkbox" label="Reset bazy rotacji na edycji" tool_tip="Gdy po raz pierwszy edytujesz obrót, zresetuj go do zera. Oznacza to, że Twoja praca może zapisać pozę (a nie różnicę, patrz ładowanie / zapisywanie). Zielony 'ptaszek' pojawia się obok każdego wyzerowanego tak, wyeksportowanego stawu." />
</panel>
</tab_container>
</panel>

View File

@ -117,7 +117,7 @@
<panel label="Ustawienia sprzętowe" name="Hardware Settings">
<check_box label="Filtrowanie anizotropowe (wolniej, gdy włączone)" name="ani" tool_tip="To pole wyboru włącza filtrowanie anizotropowe, które jest metodą polepszania jakości tekstur, gdy są one oglądane pod względnie dużymi kątami w stosunku do położenia kamery. Zazwyczaj sprawia, że wydają się mniej rozmyte przy większych odległościach." />
<check_box label="Włącz stratną kompresję tekstur (wymaga restartu)" name="texture compression" tool_tip="Kompresuje tekstury w pamięci wideo. Umożliwi to ładowanie tekstur w wyższej rozdzielczości / większej ich ilości, ale kosztem jakości obrazu."/>
<check_box label="Włącz wsparcie dla ekranów HiDPI (tylko Mac OSX; wymaga restartu)" name="use HiDPI" tool_tip="Włącz funkcje OpenGL dla rysowania w wysokich rozdzielczościach." />
<check_box label="Włącz wsparcie dla ekranów HiDPI (tylko macOS; wymaga restartu)" name="use HiDPI" tool_tip="Włącz funkcje OpenGL dla rysowania w wysokich rozdzielczościach." />
<text name="Antialiasing:" tool_tip="Zmiana tego ustawienia może wymagać ponownego uruchomienia na niektórych urządzeniach.">
Antyaliasing (zalecany restart):
</text>

View File

@ -122,7 +122,7 @@
<panel label="Аппаратные установки" name="Hardware Settings">
<check_box label="Анизотропная фильтрация (медленнее, когда включено)" name="ani" tool_tip = "Этот флажок включает анизотропную фильтрацию, которая представляет собой метод повышения качества текстур, когда они просматриваются под относительно большими углами по отношению к положению вашей камеры. Обычно заставляет их выглядеть менее размытыми на больших расстояниях."/>
<check_box label="Включить сжатие текстур с потерями (требуется перезапуск)" name="texture compression" tool_tip="Сжатие текстур в видеопамяти, позволяет текстурам с высоким разрешение загружаться за счет некоторого потери качества цвета."/>
<check_box label="Включить поддержку дисплеев HiDPI (только Mac OSX; требуется перезапуск)" name="use HiDPI" tool_tip="Включить OpenGL для отображения в высоком разрешении."/>
<check_box label="Включить поддержку дисплеев HiDPI (только macOS; требуется перезапуск)" name="use HiDPI" tool_tip="Включить OpenGL для отображения в высоком разрешении."/>
<text name="Antialiasing:" tool_tip = "Изменения этого параметра может потребовать перезапуск на некотором оборудовании." width="290">
Сглаживание (рекомендуется перезапуск):
</text>