Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fire 34747: Refactor for Base/Delta-Rotations, bug fixes #51

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions indra/newview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ set(viewer_SOURCE_FILES
fsfloatervramusage.cpp
fsfloaterwearablefavorites.cpp
fsfloaterwhitelisthelper.cpp
fsjointpose.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to add the header file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot a bunch of them... all added, thanks

fskeywords.cpp
fslslbridge.cpp
fslslbridgerequest.cpp
Expand Down
11 changes: 11 additions & 0 deletions indra/newview/app_settings/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8056,6 +8056,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>
Expand Down
260 changes: 141 additions & 119 deletions indra/newview/fsfloaterposer.cpp

Large diffs are not rendered by default.

42 changes: 30 additions & 12 deletions indra/newview/fsfloaterposer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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.
Expand All @@ -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);

Expand All @@ -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 };
Expand Down Expand Up @@ -382,7 +399,6 @@ class FSFloaterPoser : public LLFloater
LLButton* mToggleDeltaModeBtn{ nullptr };
LLButton* mRedoChangeBtn{ nullptr };
LLButton* mSetToTposeButton{ nullptr };
LLButton* mRecaptureJointsButton{ nullptr };

LLLineEditor* mPoseSaveNameEditor{ nullptr };

Expand All @@ -396,6 +412,8 @@ class FSFloaterPoser : public LLFloater
LLPanel* mMiscJointsPnl{ nullptr };
LLPanel* mCollisionVolumesPnl{ nullptr };
LLPanel* mPosesLoadSavePnl{ nullptr };
LLCheckBoxCtrl* mStopPosingOnCloseCbx{ nullptr };
LLCheckBoxCtrl* mResetBaseRotOnEditCbx{ nullptr };
};

#endif
232 changes: 232 additions & 0 deletions indra/newview/fsjointpose.cpp
Original file line number Diff line number Diff line change
@@ -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(LLVector3 scale)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scale should be passed as const LLVector3&

{
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;
}
Loading