Visual Poser v1.1 (with UI refactor)

This merges in the V1.0 poser + changes since the last beta
* a UI refactor (from angeldark raymaker)
* unified undo/redo history (angeldark raymaker)
* toggle to allow enable/disable of visual manipulator (beq)
* undo/redo keyboard accelerator support (ctrl-z/ctrl-y)
* improved focus loss/gain handing
TODO: when another tool is used (edit tools) the floater still thinks visuals are active, toggling it fixes this but it can be handled better
master
Beq 2025-03-13 13:16:52 +00:00
commit 95dcbfb8c1
25 changed files with 3179 additions and 1344 deletions

View File

@ -146,6 +146,7 @@ set(viewer_SOURCE_FILES
fslslbridgerequest.cpp
fslslpreproc.cpp
fslslpreprocviewer.cpp
fsmaniprotatejoint.cpp
fsmoneytracker.cpp
fsnamelistavatarmenu.cpp
fsnearbychatbarlistener.cpp
@ -960,6 +961,7 @@ set(viewer_HEADER_FILES
fslslpreproc.h
fslslpreprocviewer.h
fsmoneytracker.h
fsmaniprotatejoint.h
fsnamelistavatarmenu.h
fsnearbychatbarlistener.h
fsnearbychatcontrol.h

View File

@ -26198,5 +26198,16 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<real>1.0</real>
</map>
<key>FSManipRotateJointUseNaturalDirection</key>
<map>
<key>Comment</key>
<string>use the natural bone direction instead of world rotation</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
</map>
</llsd>

View File

@ -27,6 +27,7 @@
#include "fsfloaterposer.h"
#include "fsposeranimator.h"
#include "fsvirtualtrackpad.h"
#include "v4color.h"
#include "llagent.h"
#include "llavatarnamecache.h"
#include "llcheckboxctrl.h"
@ -42,6 +43,7 @@
#include "llwindow.h"
#include "llvoavatarself.h"
#include "llinventoryfunctions.h"
#include "lltoolcomp.h"
namespace
{
@ -55,7 +57,6 @@ constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_";
constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_";
constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_rotate_";
constexpr char BVH_JOINT_TRANSFORM_STRING_PREFIX[] = "bvh_joint_transform_";
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";
@ -74,10 +75,10 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.RefreshAvatars", [this](LLUICtrl*, const LLSD&) { onAvatarsRefresh(); });
mCommitCallbackRegistrar.add("Poser.StartStopAnimating", [this](LLUICtrl*, const LLSD&) { onPoseStartStop(); });
mCommitCallbackRegistrar.add("Poser.ToggleLoadSavePanel", [this](LLUICtrl*, const LLSD&) { onToggleLoadSavePanel(); });
mCommitCallbackRegistrar.add("Poser.ToggleAdvancedPanel", [this](LLUICtrl*, const LLSD&) { onToggleAdvancedPanel(); });
mCommitCallbackRegistrar.add("Poser.ToggleVisualManipulators", [this](LLUICtrl*, const LLSD&) { onToggleVisualManipulators(); });
mCommitCallbackRegistrar.add("Poser.UndoLastRotation", [this](LLUICtrl*, const LLSD&) { onUndoLastRotation(); });
mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastRotation(); });
mCommitCallbackRegistrar.add("Poser.UndoLastRotation", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); });
mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); });
mCommitCallbackRegistrar.add("Poser.ToggleMirrorChanges", [this](LLUICtrl*, const LLSD&) { onToggleMirrorChange(); });
mCommitCallbackRegistrar.add("Poser.ToggleSympatheticChanges", [this](LLUICtrl*, const LLSD&) { onToggleSympatheticChange(); });
mCommitCallbackRegistrar.add("Poser.AdjustTrackPadSensitivity", [this](LLUICtrl*, const LLSD&) { onAdjustTrackpadSensitivity(); });
@ -87,12 +88,9 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.Advanced.PositionSet", [this](LLUICtrl*, const LLSD&) { onAdvancedPositionSet(); });
mCommitCallbackRegistrar.add("Poser.Advanced.ScaleSet", [this](LLUICtrl*, const LLSD&) { onAdvancedScaleSet(); });
mCommitCallbackRegistrar.add("Poser.UndoLastPosition", [this](LLUICtrl*, const LLSD&) { onUndoLastPosition(); });
mCommitCallbackRegistrar.add("Poser.RedoLastPosition", [this](LLUICtrl*, const LLSD&) { onRedoLastPosition(); });
mCommitCallbackRegistrar.add("Poser.ResetPosition", [this](LLUICtrl*, const LLSD&) { onResetPosition(); });
mCommitCallbackRegistrar.add("Poser.ResetScale", [this](LLUICtrl*, const LLSD&) { onResetScale(); });
mCommitCallbackRegistrar.add("Poser.UndoLastScale", [this](LLUICtrl*, const LLSD&) { onUndoLastScale(); });
mCommitCallbackRegistrar.add("Poser.RedoLastScale", [this](LLUICtrl*, const LLSD&) { onRedoLastScale(); });
mCommitCallbackRegistrar.add("Poser.UndoLastPosition", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); });
mCommitCallbackRegistrar.add("Poser.RedoLastPosition", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); });
mCommitCallbackRegistrar.add("Poser.ResetJoint", [this](LLUICtrl*, const LLSD& data) { onResetJoint(data); });
mCommitCallbackRegistrar.add("Poser.Save", [this](LLUICtrl*, const LLSD&) { onClickPoseSave(); });
mCommitCallbackRegistrar.add("Pose.Menu", [this](LLUICtrl*, const LLSD& data) { onPoseMenuAction(data); });
@ -104,10 +102,9 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.FlipJoint", [this](LLUICtrl*, const LLSD&) { onClickFlipSelectedJoints(); });
mCommitCallbackRegistrar.add("Poser.RecaptureSelectedBones", [this](LLUICtrl*, const LLSD&) { onClickRecaptureSelectedBones(); });
mCommitCallbackRegistrar.add("Poser.TogglePosingSelectedBones", [this](LLUICtrl*, const LLSD&) { onClickToggleSelectedBoneEnabled(); });
mCommitCallbackRegistrar.add("Poser.PoseJointsReset", [this](LLUICtrl*, const LLSD&) { onPoseJointsReset(); });
//mCommitCallbackRegistrar.add("Poser.CommitSpinner", [this](LLUICtrl* spinnerControl, const LLSD&) { onCommitSpinner(spinnerControl); });
mCommitCallbackRegistrar.add("Poser.CommitSpinner", boost::bind(&FSFloaterPoser::onCommitSpinner, this, _1, _2));
mCommitCallbackRegistrar.add("Poser.CommitSpinner", [this](LLUICtrl* spinner, const LLSD& data) { onCommitSpinner(spinner, data); });
mCommitCallbackRegistrar.add("Poser.Symmetrize", [this](LLUICtrl*, const LLSD& data) { onClickSymmetrize(data); });
}
bool FSFloaterPoser::postBuild()
@ -115,22 +112,13 @@ bool FSFloaterPoser::postBuild()
mAvatarTrackball = getChild<FSVirtualTrackpad>("limb_rotation");
mAvatarTrackball->setCommitCallback([this](LLUICtrl *, const LLSD &) { onLimbTrackballChanged(); });
mLimbYawSlider = getChild<LLSliderCtrl>("limb_yaw");
mLimbYawSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mLimbPitchSlider = getChild<LLSliderCtrl>("limb_pitch");
mLimbPitchSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mLimbRollSlider = getChild<LLSliderCtrl>("limb_roll");
mLimbRollSlider->setCommitCallback([this](LLUICtrl *, const LLSD &) { onYawPitchRollSliderChanged(); });
mJointsTabs = getChild<LLTabContainer>("joints_tabs");
mJointsTabs->setCommitCallback(
[this](LLUICtrl*, const LLSD&)
{
onJointTabSelect();
setRotationChangeButtons(false, false);
});
});
mAvatarSelectionScrollList = getChild<LLScrollListCtrl>("avatarSelection_scroll");
mAvatarSelectionScrollList->setCommitOnSelectionChange(true);
@ -162,9 +150,8 @@ bool FSFloaterPoser::postBuild()
mPosesScrollList->setCommitOnSelectionChange(true);
mPosesScrollList->setCommitCallback([this](LLUICtrl *, const LLSD &) { onPoseFileSelect(); });
mToggleAdvancedPanelBtn = getChild<LLButton>("toggleAdvancedPanel");
if (gSavedSettings.getBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY))
mToggleAdvancedPanelBtn->setValue(true);
mToggleVisualManipulators = getChild<LLButton>("toggleVisualManipulators");
mToggleVisualManipulators->setToggleState(true);
mTrackpadSensitivitySlider = getChild<LLSliderCtrl>("trackpad_sensitivity_slider");
@ -204,7 +191,6 @@ bool FSFloaterPoser::postBuild()
mSetToTposeButton = getChild<LLButton>("set_t_pose_button");
mJointsParentPnl = getChild<LLPanel>("joints_parent_panel");
mAdvancedParentPnl = getChild<LLPanel>("advanced_parent_panel");
mTrackballPnl = getChild<LLPanel>("trackball_panel");
mPositionRotationPnl = getChild<LLPanel>("positionRotation_panel");
mBodyJointsPnl = getChild<LLPanel>("body_joints_panel");
@ -220,8 +206,11 @@ bool FSFloaterPoser::postBuild()
mTrackpadSensitivitySpnr = getChild<LLUICtrl>("trackpad_sensitivity_spinner");
mYawSpnr = getChild<LLUICtrl>("limb_yaw_spinner");
mYawSpnr->setCommitCallback([this](LLUICtrl*, const LLSD&) { onYawPitchRollChanged(); });
mPitchSpnr = getChild<LLUICtrl>("limb_pitch_spinner");
mRollSpnr = getChild<LLUICtrl>("limb_roll_spinner");
mPitchSpnr->setCommitCallback([this](LLUICtrl*, const LLSD&) { onYawPitchRollChanged(); });
mRollSpnr = getChild<LLUICtrl>("limb_roll_spinner");
mRollSpnr->setCommitCallback([this](LLUICtrl*, const LLSD&) { onYawPitchRollChanged(); });
mUpDownSpnr = getChild<LLUICtrl>("av_position_updown_spinner");
mLeftRightSpnr = getChild<LLUICtrl>("av_position_leftright_spinner");
mInOutSpnr = getChild<LLUICtrl>("av_position_inout_spinner");
@ -232,6 +221,8 @@ bool FSFloaterPoser::postBuild()
mScaleYSpnr = getChild<LLUICtrl>("adv_scaley_spinner");
mScaleZSpnr = getChild<LLUICtrl>("adv_scalez_spinner");
mBtnJointRotate = getChild<LLButton>("button_joint_rotate_tool");
return true;
}
@ -241,21 +232,54 @@ void FSFloaterPoser::onOpen(const LLSD& key)
onAvatarsRefresh();
refreshJointScrollListMembers();
onJointTabSelect();
onOpenSetAdvancedPanel();
refreshPoseScroll(mHandPresetsScrollList, POSE_PRESETS_HANDS_SUBDIRECTORY);
startPosingSelf();
enableVisualManipulators();
LLFloater::onOpen(key);
}
void FSFloaterPoser::onFocusReceived()
{
LLEditMenuHandler::gEditMenuHandler = this;
}
void FSFloaterPoser::onFocusLost()
{
if( LLEditMenuHandler::gEditMenuHandler == this )
{
LLEditMenuHandler::gEditMenuHandler = nullptr;
}
}
void FSFloaterPoser::enableVisualManipulators()
{
if (LLToolMgr::getInstance()->getCurrentToolset() != gCameraToolset)
{
mLastToolset = LLToolMgr::getInstance()->getCurrentToolset();
}
LLToolMgr::getInstance()->setCurrentToolset(gPoserToolset);
LLToolMgr::getInstance()->getCurrentToolset()->selectTool(FSToolCompPose::getInstance());
FSToolCompPose::getInstance()->setAvatar( gAgentAvatarp);
}
void FSFloaterPoser::disableVisualManipulators()
{
if (mLastToolset)
{
LLToolMgr::getInstance()->setCurrentToolset(mLastToolset);
}
FSToolCompPose::getInstance()->setAvatar(nullptr);
}
void FSFloaterPoser::onClose(bool app_quitting)
{
if (mToggleAdvancedPanelBtn)
gSavedSettings.setBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY, mToggleAdvancedPanelBtn->getValue().asBoolean());
if (gSavedSettings.getBOOL(POSER_STOPPOSINGWHENCLOSED_SAVE_KEY))
{
stopPosingAllAvatars();
}
disableVisualManipulators();
LLFloater::onClose(app_quitting);
}
@ -411,7 +435,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
{
bool savingDiff = !mPoserAnimator.allBaseRotationsAreZero(avatar);
LLSD record;
record["version"]["value"] = (S32)5;
record["version"]["value"] = (S32)6;
record["startFromTeePose"]["value"] = !savingDiff;
LLVector3 rotation, position, scale, zeroVector;
@ -573,6 +597,33 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
refreshTrackpadCursor();
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::updatePosedBones()
{
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
LLVOAvatar *avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (!currentlyPosing)
continue;
mPoserAnimator.recaptureJointAsDelta(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName()));
}
setSavePosesButtonText(true);
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onClickBrowsePoseCache()
{
@ -582,8 +633,24 @@ void FSFloaterPoser::onClickBrowsePoseCache()
gViewerWindow->getWindow()->openFile(pathname);
}
//void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner)
// Pass in an ID as a parameter, so you can use a switch statement
void FSFloaterPoser::onClickSymmetrize(S32 ID)
{
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
mPoserAnimator.symmetrizeLeftToRightOrRightToLeft(avatar, ID == 2);
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
}
void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id)
{
if (!spinner)
@ -597,8 +664,6 @@ void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id)
F32 value = (F32)spinner->getValue().asReal();
// Use the ID passed in to perform a switch statment
// which should make each action take the same amount of time.
switch (id)
{
case 0: // av_position_updown_spinner
@ -624,24 +689,6 @@ void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id)
onAdjustTrackpadSensitivity();
break;
}
case 4: // limb_pitch_spinner
{
mLimbPitchSlider->setValue(value);
onYawPitchRollSliderChanged();
break;
}
case 5: // limb_yaw_spinner
{
mLimbYawSlider->setValue(value);
onYawPitchRollSliderChanged();
break;
}
case 6: // limb_roll_spinner
{
mLimbRollSlider->setValue(value);
onYawPitchRollSliderChanged();
break;
}
case 7: // adv_posx_spinner
{
if (changingBodyPosition)
@ -690,34 +737,6 @@ void FSFloaterPoser::onCommitSpinner(LLUICtrl* spinner, S32 id)
}
}
void FSFloaterPoser::onPoseJointsReset()
{
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.resetAvatarJoint(avatar, *item);
}
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
refreshAvatarPositionSlidersAndSpinners();
}
void FSFloaterPoser::onPoseMenuAction(const LLSD& param)
{
std::string loadStyle = param.asString();
@ -757,7 +776,7 @@ void FSFloaterPoser::onPoseMenuAction(const LLSD& param)
loadPoseFromXml(avatar, poseName, loadType);
onJointTabSelect();
refreshJointScrollListMembers();
setSavePosesButtonText(mPoserAnimator.allBaseRotationsAreZero(avatar));
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
}
bool FSFloaterPoser::notDoubleClicked()
@ -946,6 +965,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
version = (S32)control_map["value"].asInteger();
}
if (version > 5 && startFromZeroRot)
mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar);
bool loadPositionsAndScalesAsDeltas = false;
if (version > 3)
loadPositionsAndScalesAsDeltas = true;
@ -1018,7 +1040,7 @@ void FSFloaterPoser::startPosingSelf()
void FSFloaterPoser::stopPosingAllAvatars()
{
if (!gAgentAvatarp || gAgentAvatarp.isNull())
if (!gAgentAvatarp || gAgentAvatarp.isNull() || !mAvatarSelectionScrollList)
return;
for (auto listItem : mAvatarSelectionScrollList->getAllData())
@ -1087,7 +1109,6 @@ bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar *avatar) const
void FSFloaterPoser::poseControlsEnable(bool enable)
{
mAdvancedParentPnl->setEnabled(enable);
mTrackballPnl->setEnabled(enable);
mFlipPoseBtn->setEnabled(enable);
mFlipJointBtn->setEnabled(enable);
@ -1254,7 +1275,7 @@ void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool toggling
refreshTrackpadCursor();
}
void FSFloaterPoser::onUndoLastRotation()
void FSFloaterPoser::onUndoLastChange()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
@ -1271,59 +1292,15 @@ void FSFloaterPoser::onUndoLastRotation()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.undoLastJointChange(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
enableOrDisableRedoButton();
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
}
void FSFloaterPoser::onUndoLastPosition()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedPositionSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
refreshAvatarPositionSlidersAndSpinners();
}
void FSFloaterPoser::onUndoLastScale()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.undoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedScaleSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
}
void FSFloaterPoser::onSetAvatarToTpose()
@ -1340,11 +1317,13 @@ void FSFloaterPoser::onSetAvatarToTpose()
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onResetPosition()
void FSFloaterPoser::onResetJoint(const LLSD data)
{
if (notDoubleClicked())
return;
int resetType = data.asInteger();
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
@ -1359,41 +1338,20 @@ void FSFloaterPoser::onResetPosition()
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.resetJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
if (!currentlyPosing)
continue;
mPoserAnimator.resetJoint(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedPositionSlidersAndSpinners();
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
refreshAvatarPositionSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
}
void FSFloaterPoser::onResetScale()
{
if (notDoubleClicked())
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.resetJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedScaleSlidersAndSpinners();
}
void FSFloaterPoser::onRedoLastRotation()
void FSFloaterPoser::onRedoLastChange()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
@ -1410,61 +1368,17 @@ void FSFloaterPoser::onRedoLastRotation()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointRotation(avatar, *item, getUiSelectedBoneDeflectionStyle());
mPoserAnimator.redoLastJointChange(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
enableOrDisableRedoButton();
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
}
void FSFloaterPoser::onRedoLastPosition()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointPosition(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
refreshAvatarPositionSlidersAndSpinners();
}
void FSFloaterPoser::onRedoLastScale()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
for (auto item : selectedJoints)
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
mPoserAnimator.redoLastJointScale(avatar, *item, getUiSelectedBoneDeflectionStyle());
}
refreshAdvancedScaleSlidersAndSpinners();
}
void FSFloaterPoser::enableOrDisableRedoButton()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
@ -1483,42 +1397,113 @@ void FSFloaterPoser::enableOrDisableRedoButton()
{
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (currentlyPosing)
shouldEnableRedoButton |= mPoserAnimator.canRedoJointRotation(avatar, *item);
shouldEnableRedoButton |= mPoserAnimator.canRedoJointChange(avatar, *item);
}
mRedoChangeBtn->setEnabled(shouldEnableRedoButton);
}
void FSFloaterPoser::onOpenSetAdvancedPanel()
void FSFloaterPoser::onToggleVisualManipulators()
{
bool advancedPanelExpanded = mToggleAdvancedPanelBtn->getValue().asBoolean();
if (advancedPanelExpanded)
onToggleAdvancedPanel();
bool tools_enabled = mToggleVisualManipulators->getValue().asBoolean();
if (tools_enabled)
{
enableVisualManipulators();
}
else
{
disableVisualManipulators();
}
}
void FSFloaterPoser::onToggleAdvancedPanel()
void FSFloaterPoser::selectJointByName(const std::string& jointName)
{
if (isMinimized())
return;
LLTabContainer* tabContainer = mJointsTabs;
std::vector<LLPanel*> panels = {
mPositionRotationPnl,
mBodyJointsPnl,
mFaceJointsPnl,
mHandsTabs,
mMiscJointsPnl,
mCollisionVolumesPnl
};
std::vector<LLScrollListCtrl*> scrollLists = {
mEntireAvJointScroll,
mBodyJointsScrollList,
mFaceJointsScrollList,
mHandJointsScrollList,
mMiscJointsScrollList,
mCollisionVolumesScrollList
};
bool advancedPanelExpanded = mToggleAdvancedPanelBtn->getValue().asBoolean();
bool found = false;
for (S32 i = 0; i < tabContainer->getTabCount(); ++i)
{
LLPanel* panel = tabContainer->getPanelByIndex(i);
tabContainer->selectTabPanel(panel);
mAdvancedParentPnl->setVisible(advancedPanelExpanded);
// Special handling for Hands tab
if (panel == mHandsTabs)
{
mHandsTabs->selectTabPanel(mHandsJointsPnl);
}
// change the height of the Poser panel
S32 currentHeight = getRect().getHeight();
S32 advancedPanelHeight = mAdvancedParentPnl->getRect().getHeight();
for (auto scrollList : scrollLists)
{
scrollList->deselectAllItems();
}
S32 poserFloaterHeight = advancedPanelExpanded ? currentHeight + advancedPanelHeight : currentHeight - advancedPanelHeight;
S32 poserFloaterWidth = getRect().getWidth();
auto scrollList = getScrollListForTab(panel);
if (poserFloaterHeight < 0)
return;
reshape(poserFloaterWidth, poserFloaterHeight);
onJointTabSelect();
std::vector<LLScrollListItem*> items = scrollList->getAllData();
for (auto item : items)
{
auto* userData = static_cast<FSPoserAnimator::FSPoserJoint*>(item->getUserdata());
if (userData && userData->jointName() == jointName)
{
tabContainer->selectTab(i);
scrollList->selectNthItem(scrollList->getItemIndex(item));
scrollList->scrollToShowSelected();
getUiSelectedPoserJoints();
return; // Exit the loop once we've found and selected the joint
}
}
}
LL_WARNS() << "Joint not found: " << jointName << LL_ENDL;
}
LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel * tabPanel) const
{
if (tabPanel == mPositionRotationPnl)
{
return mEntireAvJointScroll;
}
else if (tabPanel == mBodyJointsPnl)
{
return mBodyJointsScrollList;
}
else if (tabPanel == mFaceJointsPnl)
{
return mFaceJointsScrollList;
}
else if (tabPanel == mHandsTabs)
{
return mHandJointsScrollList;
}
else if (tabPanel == mMiscJointsPnl)
{
return mMiscJointsScrollList;
}
else if (tabPanel == mCollisionVolumesPnl)
{
return mCollisionVolumesScrollList;
}
LL_WARNS() << "Unknown tab panel: " << tabPanel << LL_ENDL;
return nullptr;
}
std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJoints() const
{
std::vector<FSPoserAnimator::FSPoserJoint*> joints;
@ -1530,42 +1515,20 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
}
LLScrollListCtrl* scrollList{ nullptr };
scrollList = getScrollListForTab(activeTab);
if (activeTab == mPositionRotationPnl)
{
mEntireAvJointScroll->selectFirstItem();
scrollList = mEntireAvJointScroll;
}
else if (activeTab == mBodyJointsPnl)
{
scrollList = mBodyJointsScrollList;
}
else if (activeTab == mFaceJointsPnl)
{
scrollList = mFaceJointsScrollList;
}
else if (activeTab == mHandsTabs)
else if (activeTab == mHandsTabs )
{
auto activeHandsSubTab = mHandsTabs->getCurrentPanel();
if (!activeHandsSubTab)
{
return joints;
}
if (activeHandsSubTab == mHandsJointsPnl)
{
scrollList = mHandJointsScrollList;
}
}
else if (activeTab == mMiscJointsPnl)
{
scrollList = mMiscJointsScrollList;
}
else if (activeTab == mCollisionVolumesPnl)
{
scrollList = mCollisionVolumesScrollList;
}
if (!scrollList)
{
return joints;
@ -1579,7 +1542,18 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
joints.push_back(userData);
}
}
auto avatarp = getUiSelectedAvatar();
if (avatarp)
{
if(joints.size() >= 1)
{
FSToolCompPose::getInstance()->setJoint( gAgentAvatarp->getJoint( JointKey::construct(joints[0]->jointName())) );
}
else
{
FSToolCompPose::getInstance()->setJoint( nullptr );
}
}
return joints;
}
@ -1733,7 +1707,7 @@ void FSFloaterPoser::onAvatarPositionSet()
mUpDownSpnr->setValue(posZ);
setSelectedJointsPosition(posX, posY, posZ);
refreshAdvancedPositionSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
}
void FSFloaterPoser::onLimbTrackballChanged()
@ -1769,13 +1743,9 @@ void FSFloaterPoser::onLimbTrackballChanged()
// as tempting as it is to refactor the following to refreshRotationSliders(), don't.
// getRotationOfFirstSelectedJoint/setSelectedJointsRotation are
// not necessarily symmetric functions (see their remarks).
mLimbYawSlider->setValue(trackPadPos.mV[VX] *= RAD_TO_DEG);
mLimbPitchSlider->setValue(trackPadPos.mV[VY] *= RAD_TO_DEG);
mLimbRollSlider->setValue(trackPadPos.mV[VZ] *= RAD_TO_DEG);
mYawSpnr->setValue(mLimbYawSlider->getValueF32());
mPitchSpnr->setValue(mLimbPitchSlider->getValueF32());
mRollSpnr->setValue(mLimbRollSlider->getValueF32());
mYawSpnr->setValue(trackPadPos.mV[VX] *= RAD_TO_DEG);
mPitchSpnr->setValue(trackPadPos.mV[VY] *= RAD_TO_DEG);
mRollSpnr->setValue(trackPadPos.mV[VZ] *= RAD_TO_DEG);
}
F32 FSFloaterPoser::unWrapScale(F32 scale)
@ -1792,12 +1762,12 @@ F32 FSFloaterPoser::unWrapScale(F32 scale)
return result;
}
void FSFloaterPoser::onYawPitchRollSliderChanged()
void FSFloaterPoser::onYawPitchRollChanged()
{
LLVector3 absoluteRotation, deltaRotation;
absoluteRotation.mV[VX] = mLimbYawSlider->getValueF32() * DEG_TO_RAD;
absoluteRotation.mV[VY] = mLimbPitchSlider->getValueF32() * DEG_TO_RAD;
absoluteRotation.mV[VZ] = mLimbRollSlider->getValueF32() * DEG_TO_RAD;
absoluteRotation.mV[VX] = (F32)mYawSpnr->getValue().asReal() * DEG_TO_RAD;
absoluteRotation.mV[VY] = (F32)mPitchSpnr->getValue().asReal() * DEG_TO_RAD;
absoluteRotation.mV[VZ] = (F32)mRollSpnr->getValue().asReal() * DEG_TO_RAD;
deltaRotation = absoluteRotation - mLastSliderRotation;
mLastSliderRotation = absoluteRotation;
@ -1817,10 +1787,6 @@ void FSFloaterPoser::onYawPitchRollSliderChanged()
absoluteRotation.mV[VZ] /= NormalTrackpadRangeInRads;
mAvatarTrackball->setValue(absoluteRotation.getValue());
mYawSpnr->setValue(mLimbYawSlider->getValueF32());
mPitchSpnr->setValue(mLimbPitchSlider->getValueF32());
mRollSpnr->setValue(mLimbRollSlider->getValueF32());
}
void FSFloaterPoser::onAdjustTrackpadSensitivity()
@ -1830,9 +1796,9 @@ void FSFloaterPoser::onAdjustTrackpadSensitivity()
void FSFloaterPoser::refreshTrackpadCursor()
{
F32 axis1 = mLimbYawSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis2 = mLimbPitchSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis3 = mLimbRollSlider->getValueF32() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis1 = (F32)mYawSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis2 = (F32)mPitchSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 axis3 = (F32)mRollSpnr->getValue().asReal() * DEG_TO_RAD / NormalTrackpadRangeInRads;
F32 trackPadSensitivity = llmax(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY), 0.0001f);
axis1 /= trackPadSensitivity;
@ -1867,15 +1833,12 @@ void FSFloaterPoser::refreshRotationSlidersAndSpinners()
LLVector3 rotation = getRotationOfFirstSelectedJoint();
mLastSliderRotation = rotation;
mLimbYawSlider->setValue(rotation.mV[VX] *= RAD_TO_DEG);
mYawSpnr->setValue(rotation.mV[VX]);
mLimbPitchSlider->setValue(rotation.mV[VY] *= RAD_TO_DEG);
mPitchSpnr->setValue(rotation.mV[VY]);
mLimbRollSlider->setValue(rotation.mV[VZ] *= RAD_TO_DEG);
mRollSpnr->setValue(rotation.mV[VZ]);
mYawSpnr->setValue(rotation.mV[VX] *= RAD_TO_DEG);
mPitchSpnr->setValue(rotation.mV[VY] *= RAD_TO_DEG);
mRollSpnr->setValue(rotation.mV[VZ] *= RAD_TO_DEG);
}
void FSFloaterPoser::refreshAdvancedPositionSlidersAndSpinners()
void FSFloaterPoser::refreshPositionSlidersAndSpinners()
{
LLVector3 position = getPositionOfFirstSelectedJoint();
@ -1887,7 +1850,7 @@ void FSFloaterPoser::refreshAdvancedPositionSlidersAndSpinners()
mAdvPosZSpnr->setValue(position.mV[VZ]);
}
void FSFloaterPoser::refreshAdvancedScaleSlidersAndSpinners()
void FSFloaterPoser::refreshScaleSlidersAndSpinners()
{
LLVector3 rotation = getScaleOfFirstSelectedJoint();
@ -2041,16 +2004,12 @@ LLVector3 FSFloaterPoser::getScaleOfFirstSelectedJoint() const
void FSFloaterPoser::onJointTabSelect()
{
refreshAvatarPositionSlidersAndSpinners();
refreshRotationSlidersAndSpinners();
refreshRotationSlidersAndSpinners();
refreshTrackpadCursor();
enableOrDisableRedoButton();
refreshPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
onClickSetBaseRotZero();
if (mToggleAdvancedPanelBtn->getValue().asBoolean())
{
refreshAdvancedPositionSlidersAndSpinners();
refreshAdvancedScaleSlidersAndSpinners();
}
}
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const
@ -2110,6 +2069,10 @@ S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
void FSFloaterPoser::onAvatarSelect()
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if(avatar)
{
FSToolCompPose::getInstance()->setAvatar(avatar);
}
mStartStopPosingBtn->setEnabled(couldAnimateAvatar(avatar));
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);

View File

@ -29,6 +29,7 @@
#define FS_FLOATER_POSER_H
#include "llfloater.h"
#include "lltoolmgr.h"
#include "fsposeranimator.h"
class FSVirtualTrackpad;
@ -73,16 +74,23 @@ typedef enum E_Columns
/// A class containing the UI fiddling for the Poser Floater.
/// Please don't do LLJoint stuff here, fsposingmotion (the LLMotion derivative) is the class for that.
/// </summary>
class FSFloaterPoser : public LLFloater
class FSFloaterPoser : public LLFloater, public LLEditMenuHandler
{
friend class LLFloaterReg;
FSFloaterPoser(const LLSD &key);
public:
void updatePosedBones();
void selectJointByName(const std::string& jointName);
void undo() override { onUndoLastChange(); };
bool canUndo() const override { return true; }
void redo() override { onRedoLastChange(); };
bool canRedo() const override { return true; }
private:
bool postBuild() override;
void onOpen(const LLSD& key) override;
void onClose(bool app_quitting) override;
void onFocusReceived() override;
void onFocusLost() override;
/// <summary>
/// Refreshes the supplied pose list from the supplued subdirectory.
/// </summary>
@ -176,7 +184,7 @@ class FSFloaterPoser : public LLFloater
/// <param name="toFind">The avatar UUID to find on the avatars scroll list.</param>
/// <returns>The scroll-list index for the supplied avatar, if found, otherwise -1.</returns>
S32 getAvatarListIndexForUuid(const LLUUID& toFind) const;
/// <summary>
/// There are several control-callbacks manipulating rotations etc, they all devolve to these.
/// In these are the appeals to the posing business layer.
@ -185,11 +193,10 @@ class FSFloaterPoser : public LLFloater
/// Using a set, then a get does not guarantee the value you just set.
/// There may be +/- PI difference two axes, because harmonics.
/// Thus keep your UI synced with less gets.
/// </remarks>
/// </remarks>
void setSelectedJointsRotation(const LLVector3& absoluteRot, const LLVector3& deltaRot);
void setSelectedJointsPosition(F32 x, F32 y, F32 z);
void setSelectedJointsScale(F32 x, F32 y, F32 z);
/// <summary>
/// Yeilds the rotation of the first selected joint (one may multi-select).
/// </summary>
@ -202,6 +209,7 @@ class FSFloaterPoser : public LLFloater
LLVector3 getPositionOfFirstSelectedJoint() const;
LLVector3 getScaleOfFirstSelectedJoint() const;
LLScrollListCtrl* getScrollListForTab(LLPanel * tabPanel) const;
// Pose load/save
void createUserPoseDirectoryIfNeeded();
void onToggleLoadSavePanel();
@ -215,30 +223,28 @@ class FSFloaterPoser : public LLFloater
bool poseFileStartsFromTeePose(const std::string& poseFileName);
void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName();
void setUiSelectedAvatarSaveFileName(const std::string& saveFileName);
// visual manipulators control
void enableVisualManipulators();
void disableVisualManipulators();
// UI Event Handlers:
void onAvatarsRefresh();
void onAvatarSelect();
void onJointTabSelect();
void onToggleAdvancedPanel();
void onToggleMirrorChange();
void onToggleSympatheticChange();
void onToggleVisualManipulators();
void setRotationChangeButtons(bool mirror, bool sympathetic);
void onUndoLastRotation();
void onRedoLastRotation();
void onUndoLastPosition();
void onRedoLastPosition();
void onUndoLastScale();
void onRedoLastScale();
void onResetPosition();
void onResetScale();
void onUndoLastChange();
void onRedoLastChange();
void onResetJoint(const LLSD data);
void onSetAvatarToTpose();
void enableOrDisableRedoButton();
void onPoseStartStop();
void startPosingSelf();
void stopPosingAllAvatars();
void onLimbTrackballChanged();
void onYawPitchRollSliderChanged();
void onYawPitchRollChanged();
void onAvatarPositionSet();
void onAdvancedPositionSet();
void onAdvancedScaleSet();
@ -246,22 +252,20 @@ class FSFloaterPoser : public LLFloater
void onClickRecaptureSelectedBones();
void onClickFlipPose();
void onClickFlipSelectedJoints();
void onPoseJointsReset();
void onOpenSetAdvancedPanel();
void onAdjustTrackpadSensitivity();
void onClickLoadLeftHandPose();
void onClickLoadRightHandPose();
void onClickLoadHandPose(bool isRightHand);
void onClickSetBaseRotZero();
//void onCommitSpinner(LLUICtrl* spinner);
void onCommitSpinner(LLUICtrl* spinner, S32 ID);
void onClickSymmetrize(S32 ID);
// UI Refreshments
void refreshRotationSlidersAndSpinners();
void refreshAvatarPositionSlidersAndSpinners();
void refreshTrackpadCursor();
void refreshAdvancedPositionSlidersAndSpinners();
void refreshAdvancedScaleSlidersAndSpinners();
void refreshPositionSlidersAndSpinners();
void refreshScaleSlidersAndSpinners();
/// <summary>
/// Determines if we have permission to animate the supplied avatar.
@ -443,13 +447,14 @@ class FSFloaterPoser : public LLFloater
/// </remarks>
static F32 unWrapScale(F32 scale);
LLToolset* mLastToolset{ nullptr };
LLTool* mJointRotTool{ nullptr };
LLVector3 mLastSliderRotation;
FSVirtualTrackpad* mAvatarTrackball{ nullptr };
LLSliderCtrl* mTrackpadSensitivitySlider{ nullptr };
LLSliderCtrl* mLimbYawSlider{ nullptr };
LLSliderCtrl* mLimbPitchSlider{ nullptr }; // pointing your nose up or down
LLSliderCtrl* mLimbRollSlider{ nullptr }; // your ear touches your shoulder
LLSliderCtrl* mPosXSlider{ nullptr };
LLSliderCtrl* mPosYSlider{ nullptr };
LLSliderCtrl* mPosZSlider{ nullptr };
@ -473,7 +478,7 @@ class FSFloaterPoser : public LLFloater
LLScrollListCtrl* mPosesScrollList{ nullptr };
LLScrollListCtrl* mHandPresetsScrollList{ nullptr };
LLButton* mToggleAdvancedPanelBtn{ nullptr };
LLButton* mToggleVisualManipulators{ nullptr };
LLButton* mStartStopPosingBtn{ nullptr };
LLButton* mToggleLoadSavePanelBtn{ nullptr };
LLButton* mBrowserFolderBtn{ nullptr };
@ -488,10 +493,10 @@ class FSFloaterPoser : public LLFloater
LLButton* mToggleDeltaModeBtn{ nullptr };
LLButton* mRedoChangeBtn{ nullptr };
LLButton* mSetToTposeButton{ nullptr };
LLButton* mBtnJointRotate{ nullptr };
LLLineEditor* mPoseSaveNameEditor{ nullptr };
LLPanel* mAdvancedParentPnl{ nullptr };
LLPanel* mJointsParentPnl{ nullptr };
LLPanel* mTrackballPnl{ nullptr };
LLPanel* mPositionRotationPnl{ nullptr };

View File

@ -30,7 +30,7 @@
#include "llcharacter.h"
/// <summary>
/// The maximum length of any undo queue; adding new members preens older ones.
/// The maximum length of the undo queue; adding new members preens older ones.
/// </summary>
constexpr size_t MaximumUndoQueueLength = 20;
@ -48,103 +48,86 @@ FSJointPose::FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume)
mJointName = joint->getName();
mIsCollisionVolume = isCollisionVolume;
mRotation = FSJointRotation(joint->getRotation());
mBeginningPosition = joint->getPosition();
mBeginningScale = joint->getScale();
mCurrentState = FSJointState(joint);
}
void FSJointPose::setPositionDelta(const LLVector3& pos)
void FSJointPose::setPublicPosition(const LLVector3& pos)
{
addToUndo(mPositionDelta, &mUndonePositionIndex, &mLastSetPositionDeltas, &mTimeLastUpdatedPosition);
mPositionDelta.set(pos);
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.mPosition.set(pos);
}
void FSJointPose::setRotationDelta(const LLQuaternion& rot)
void FSJointPose::setPublicRotation(const LLQuaternion& rot)
{
addToUndo(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas, &mTimeLastUpdatedRotation);
mRotation = FSJointRotation(mRotation.baseRotation, rot);
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.mRotation.set(rot);
}
void FSJointPose::setScaleDelta(const LLVector3& scale)
void FSJointPose::setPublicScale(const LLVector3& scale)
{
addToUndo(mScaleDelta, &mUndoneScaleIndex, &mLastSetScaleDeltas, &mTimeLastUpdatedScale);
mScaleDelta.set(scale);
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.mScale.set(scale);
}
void FSJointPose::undoLastPositionChange()
void FSJointPose::undoLastChange()
{
mPositionDelta.set(undoLastChange(mPositionDelta, &mUndonePositionIndex, &mLastSetPositionDeltas));
mCurrentState = undoLastStateChange(FSJointState(mCurrentState));
}
void FSJointPose::undoLastRotationChange()
void FSJointPose::redoLastChange()
{
mRotation.set(undoLastChange(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas));
mCurrentState = redoLastStateChange(FSJointState(mCurrentState));
}
void FSJointPose::undoLastScaleChange() { mScaleDelta.set(undoLastChange(mScaleDelta, &mUndoneScaleIndex, &mLastSetScaleDeltas)); }
void FSJointPose::redoLastPositionChange()
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
{
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();
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState;
mTimeLastUpdatedCurrentState = std::chrono::system_clock::now();
if (timeIntervalSinceLastChange < UndoUpdateInterval)
return;
if (*undoIndex > 0)
if (mUndoneJointStatesIndex > 0)
{
for (size_t i = 0; i < *undoIndex; i++)
dequeue->pop_front();
for (size_t i = 0; i <= mUndoneJointStatesIndex; i++)
if (!mLastSetJointStates.empty())
mLastSetJointStates.pop_front();
*undoIndex = 0;
mUndoneJointStatesIndex = 0;
}
dequeue->push_front(delta);
mLastSetJointStates.push_front(stateToAddToUndo);
while (dequeue->size() > MaximumUndoQueueLength)
dequeue->pop_back();
while (mLastSetJointStates.size() > MaximumUndoQueueLength)
mLastSetJointStates.pop_back();
}
template <typename T> T FSJointPose::undoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue)
FSJointPose::FSJointState FSJointPose::undoLastStateChange(FSJointState thingToSet)
{
if (dequeue->empty())
if (mLastSetJointStates.empty())
return thingToSet;
if (*undoIndex == 0)
dequeue->push_front(thingToSet);
if (mUndoneJointStatesIndex == 0)
mLastSetJointStates.push_front(thingToSet);
*undoIndex += 1;
*undoIndex = llclamp(*undoIndex, 0, dequeue->size() - 1);
mUndoneJointStatesIndex += 1;
mUndoneJointStatesIndex = llclamp(mUndoneJointStatesIndex, 0, mLastSetJointStates.size() - 1);
return dequeue->at(*undoIndex);
return mLastSetJointStates.at(mUndoneJointStatesIndex);
}
template <typename T> T FSJointPose::redoLastChange(T thingToSet, size_t* undoIndex, std::deque<T>* dequeue)
FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToSet)
{
if (dequeue->empty())
if (mLastSetJointStates.empty())
return thingToSet;
if (*undoIndex == 0)
if (mUndoneJointStatesIndex == 0)
return thingToSet;
*undoIndex -= 1;
*undoIndex = llclamp(*undoIndex, 0, dequeue->size() - 1);
T result = dequeue->at(*undoIndex);
if (*undoIndex == 0)
dequeue->pop_front();
mUndoneJointStatesIndex -= 1;
mUndoneJointStatesIndex = llclamp(mUndoneJointStatesIndex, 0, mLastSetJointStates.size() - 1);
auto result = mLastSetJointStates.at(mUndoneJointStatesIndex);
if (mUndoneJointStatesIndex == 0)
mLastSetJointStates.pop_front();
return result;
}
@ -158,8 +141,24 @@ void FSJointPose::recaptureJoint()
if (!joint)
return;
addToUndo(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas, &mTimeLastUpdatedRotation);
mRotation = FSJointRotation(joint->getRotation());
addStateToUndo(FSJointState(mCurrentState));
mCurrentState = FSJointState(joint);
}
void FSJointPose::recaptureJointAsDelta()
{
if (mIsCollisionVolume)
{
return;
}
LLJoint* joint = mJointState->getJoint();
if (!joint)
{
return;
}
addStateToUndo(mCurrentState);
mCurrentState = FSJointState(joint);
}
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
@ -169,9 +168,9 @@ void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
if (mIsCollisionVolume)
return;
auto tempRot = FSJointRotation(mRotation);
mRotation = FSJointRotation(oppositeJoint->mRotation);
oppositeJoint->mRotation = tempRot;
auto tempState = FSJointState(mCurrentState);
mCurrentState.cloneRotationFrom(oppositeJoint->mCurrentState);
oppositeJoint->mCurrentState.cloneRotationFrom(tempState);
}
void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint)
@ -179,8 +178,8 @@ void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint)
if (!fromJoint)
return;
addToUndo(mRotation, &mUndoneRotationIndex, &mLastSetRotationDeltas, &mTimeLastUpdatedRotation);
mRotation = FSJointRotation(fromJoint->mRotation);
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.cloneRotationFrom(fromJoint->mCurrentState);
}
void FSJointPose::mirrorRotationFrom(FSJointPose* fromJoint)
@ -189,22 +188,12 @@ void FSJointPose::mirrorRotationFrom(FSJointPose* fromJoint)
return;
cloneRotationFrom(fromJoint);
mRotation.baseRotation = LLQuaternion(-mRotation.baseRotation.mQ[VX], mRotation.baseRotation.mQ[VY], -mRotation.baseRotation.mQ[VZ],
mRotation.baseRotation.mQ[VW]);
mRotation.deltaRotation = LLQuaternion(-mRotation.deltaRotation.mQ[VX], mRotation.deltaRotation.mQ[VY], -mRotation.deltaRotation.mQ[VZ],
mRotation.deltaRotation.mQ[VW]);
mCurrentState.reflectRotation();
}
void FSJointPose::revertJoint()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setRotation(mRotation.baseRotation);
joint->setPosition(mBeginningPosition);
joint->setScale(mBeginningScale);
mCurrentState.revertJointToBase(mJointState->getJoint());
}
void FSJointPose::reflectRotation()
@ -212,7 +201,7 @@ void FSJointPose::reflectRotation()
if (mIsCollisionVolume)
return;
mRotation.reflectRotation();
mCurrentState.reflectRotation();
}
void FSJointPose::zeroBaseRotation()
@ -220,7 +209,7 @@ void FSJointPose::zeroBaseRotation()
if (mIsCollisionVolume)
return;
mRotation.baseRotation = LLQuaternion::DEFAULT;
mCurrentState.zeroBaseRotation();
}
bool FSJointPose::isBaseRotationZero() const
@ -228,5 +217,5 @@ bool FSJointPose::isBaseRotationZero() const
if (mIsCollisionVolume)
return true;
return mRotation.baseRotation == LLQuaternion::DEFAULT;
return mCurrentState.baseRotationIsZero();
}

View File

@ -58,34 +58,39 @@ class FSJointPose
bool isCollisionVolume() const { return mIsCollisionVolume; }
/// <summary>
/// Gets the position change the animator wishes the joint to have.
/// Gets the 'public' position of the joint.
/// </summary>
LLVector3 getPositionDelta() const { return mPositionDelta; }
LLVector3 getPublicPosition() const { return mCurrentState.mPosition; }
/// <summary>
/// Sets the position the animator wishes the joint to be in.
/// Sets the 'public' position of the joint.
/// </summary>
void setPositionDelta(const LLVector3& pos);
void setPublicPosition(const LLVector3& pos);
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void undoLastPositionChange();
void undoLastChange();
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void redoLastPositionChange();
void redoLastChange();
/// <summary>
/// Gets the rotation the animator wishes the joint to be in.
/// Gets the 'public' rotation of the joint.
/// </summary>
LLQuaternion getRotationDelta() const { return mRotation.deltaRotation; }
LLQuaternion getPublicRotation() const { return mCurrentState.mRotation; }
/// <summary>
/// Sets the rotation the animator wishes the joint to be in.
/// Sets the 'public' rotation of the joint.
/// </summary>
void setRotationDelta(const LLQuaternion& rot);
/// <remarks>
/// 'Public rotation' is the amount of rotation the user has added to the initial state.
/// Public rotation is what a user may save to an external format (such as BVH).
/// This distinguishes 'private' rotation, which is the state inherited from something like a pose in-world.
/// </remarks>
void setPublicRotation(const LLQuaternion& rot);
/// <summary>
/// Reflects the base and delta rotation of the represented joint left-right.
@ -93,7 +98,7 @@ class FSJointPose
void reflectRotation();
/// <summary>
/// Sets the base rotation of the represented joint to zero.
/// Sets the private rotation of the represented joint to zero.
/// </summary>
void zeroBaseRotation();
@ -104,43 +109,20 @@ class FSJointPose
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.
/// Gets whether a redo of this joint may be performed.
/// </summary>
void undoLastRotationChange();
/// <returns>true if the joint may have a redo applied, otherwise false.</returns>
bool canPerformRedo() const { return mUndoneJointStatesIndex > 0; }
/// <summary>
/// Redoes the last rotation set, if any.
/// Gets the 'public' scale of the joint.
/// </summary>
void redoLastRotationChange();
LLVector3 getPublicScale() const { return mCurrentState.mScale; }
/// <summary>
/// Gets whether a redo of this joints rotation may be performed.
/// Sets the 'public' scale of the joint.
/// </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();
void setPublicScale(const LLVector3& scale);
/// <summary>
/// Exchanges the rotations between two joints.
@ -161,6 +143,11 @@ class FSJointPose
/// Resets the beginning properties of the joint this represents.
/// </summary>
void recaptureJoint();
/// <summary>
/// Recalculates the delta reltive to the base for a new rotation.
/// </summary>
void recaptureJointAsDelta();
/// <summary>
/// Reverts the position/rotation/scale to their values when the animation begun.
@ -168,52 +155,90 @@ class FSJointPose
/// </summary>
void revertJoint();
LLVector3 getTargetPosition() const { return mPositionDelta + mBeginningPosition; }
LLQuaternion getTargetRotation() const { return mRotation.getTargetRotation(); }
LLVector3 getTargetScale() const { return mScaleDelta + mBeginningScale; }
LLQuaternion getTargetRotation() const { return mCurrentState.getTargetRotation(); }
LLVector3 getTargetPosition() const { return mCurrentState.getTargetPosition(); }
LLVector3 getTargetScale() const { return mCurrentState.getTargetScale(); }
/// <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
class FSJointState
{
public:
FSJointRotation(LLQuaternion base) { baseRotation.set(base); }
FSJointRotation(LLQuaternion base, LLQuaternion delta)
FSJointState(LLJoint* joint)
{
baseRotation.set(base);
deltaRotation.set(delta);
mBaseRotation.set(joint->getRotation());
mBasePosition.set(joint->getPosition());
mBaseScale.set(joint->getScale());
}
FSJointRotation() = default;
LLQuaternion baseRotation;
LLQuaternion deltaRotation;
LLQuaternion getTargetRotation() const { return deltaRotation * baseRotation; }
FSJointState() = default;
LLQuaternion mDeltaRotation;
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
void updateRotation(const LLQuaternion& newRotation)
{
auto inv_base = mBaseRotation;
inv_base.conjugate();
mDeltaRotation = newRotation * inv_base;
};
void reflectRotation()
{
baseRotation.mQ[VX] *= -1;
baseRotation.mQ[VZ] *= -1;
deltaRotation.mQ[VX] *= -1;
deltaRotation.mQ[VZ] *= -1;
mBaseRotation.mQ[VX] *= -1;
mBaseRotation.mQ[VZ] *= -1;
mRotation.mQ[VX] *= -1;
mRotation.mQ[VZ] *= -1;
}
void set(const FSJointRotation& jRot)
void cloneRotationFrom(FSJointState otherState)
{
baseRotation.set(jRot.baseRotation);
deltaRotation.set(jRot.deltaRotation);
mBaseRotation.set(otherState.mBaseRotation);
mRotation.set(otherState.mRotation);
}
bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; }
void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; }
void revertJointToBase(LLJoint* joint) const
{
if (!joint)
return;
joint->setRotation(mBaseRotation);
joint->setPosition(mBasePosition);
joint->setScale(mBaseScale);
}
private:
FSJointState(FSJointState* state)
{
mBaseRotation.set(state->mBaseRotation);
mBasePosition.set(state->mBasePosition);
mBaseScale.set(state->mBaseScale);
mRotation.set(state->mRotation);
mPosition.set(state->mPosition);
mScale.set(state->mScale);
}
public:
LLQuaternion mRotation;
LLVector3 mPosition;
LLVector3 mScale;
private:
LLQuaternion mBaseRotation;
LLVector3 mBasePosition;
LLVector3 mBaseScale;
};
private:
private:
std::string mJointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation.
LLPointer<LLJointState> mJointState{ nullptr };
@ -223,32 +248,15 @@ private:
/// </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();
std::deque<FSJointState> mLastSetJointStates;
size_t mUndoneJointStatesIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedCurrentState = 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();
FSJointState mCurrentState;
/// <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);
void addStateToUndo(FSJointState stateToAddToUndo);
FSJointState undoLastStateChange(FSJointState currentState);
FSJointState redoLastStateChange(FSJointState currentState);
};
#endif // FS_JOINTPPOSE_H

View File

@ -0,0 +1,66 @@
#include "fsjointrotatetool.h"
#include "llviewerobject.h"
#include "llvoavatar.h"
#include "llviewercontrol.h"
FSJointRotateTool::FSJointRotateTool() : LLTool(std::string("Rotate Joint"))
{
}
void FSJointRotateTool::handleSelect()
{
LL_INFOS() << "Rotate tool selected" << LL_ENDL;
}
void FSJointRotateTool::handleDeselect()
{
LL_INFOS() << "Rotate tool deselected" << LL_ENDL;
}
bool FSJointRotateTool::handleMouseDown(S32 x, S32 y, MASK mask)
{
if (findSelectedManipulator(x, y))
{
LLVOAvatar* avatar = gAgentAvatarp; // Assume the avatar is the agent avatar
if (avatar)
{
rotateJoint(avatar);
}
}
return true;
}
bool FSJointRotateTool::handleHover(S32 x, S32 y, MASK mask)
{
return findSelectedManipulator(x, y);
}
void FSJointRotateTool::render()
{
computeManipulatorSize();
renderManipulators();
}
void FSJointRotateTool::rotateJoint(LLVOAvatar* avatar)
{
// Use joint rotation APIs, perhaps from FSPoserAnimator
LL_INFOS() << "Rotating joint" << LL_ENDL;
}
bool FSJointRotateTool::findSelectedManipulator(S32 x, S32 y)
{
// Implement logic to detect user selection of a manipulator
return false;
}
void FSJointRotateTool::computeManipulatorSize()
{
mManipulatorSize = 5.0f; // Example size
}
void FSJointRotateTool::renderManipulators()
{
// Render manipulators (axes or rotation handles) using OpenGL
gGL.color4f(0.5f, 0.5f, 0.5f, 0.5f); // Example color
gGL.flush();
}

View File

@ -0,0 +1,32 @@
#ifndef FS_JOINTROTATETOOL_H
#define FS_JOINTROTATETOOL_H
#include "lltool.h"
#include "llbbox.h"
#include "llvoavatar.h"
class FSJointRotateTool : public LLTool, public LLSingleton<FSJointRotateTool>
{
LLSINGLETON(FSJointRotateTool);
public:
void handleSelect() override;
void handleDeselect() override;
bool handleMouseDown(S32 x, S32 y, MASK mask) override;
bool handleHover(S32 x, S32 y, MASK mask) override;
void render() override;
private:
void rotateJoint(LLVOAvatar* avatar);
bool findSelectedManipulator(S32 x, S32 y);
void computeManipulatorSize();
void renderManipulators();
LLBBox mBBox;
F32 mManipulatorSize;
S32 mHighlightedAxis;
F32 mHighlightedDirection;
bool mForce;
};
#endif // FS_JOINTROTATETOOL_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
// File: fsmaniprotatejoint.h
#ifndef FS_MANIP_ROTATE_JOINT_H
#define FS_MANIP_ROTATE_JOINT_H
#include "llselectmgr.h"
#include "llmaniprotate.h"
class LLJoint;
class LLVOAvatar; // or LLVOAvatarSelf, etc.
namespace {
const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD ); // cos() is not constexpr til c++26
constexpr F32 RADIUS_PIXELS = 100.f; // size in screen space
constexpr S32 CIRCLE_STEPS = 100;
constexpr F32 CIRCLE_STEP_SIZE = 2.0f * F_PI / CIRCLE_STEPS;
constexpr F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
constexpr F32 WIDTH_PIXELS = 8;
constexpr F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
constexpr F32 SNAP_ANGLE_INCREMENT = 5.625f;
constexpr F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
constexpr F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
constexpr F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
constexpr F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
constexpr F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
constexpr F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
constexpr F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
constexpr F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
constexpr F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
constexpr S32 VERTICAL_OFFSET = 100;
}
class FSManipRotateJoint : public LLManipRotate
{
// Used for overriding the natural "up" direction of a joint.
// if no override is set then the world up direction is used unless the joint is vertical in which case we pick an arbitrary normal
static std::unordered_map<std::string, LLVector3> sReferenceUpVectors;
struct BoneAxes
{
LLVector3 naturalX;
LLVector3 naturalY;
LLVector3 naturalZ;
};
LLQuaternion computeAlignmentQuat( const BoneAxes& boneAxes ) const;
BoneAxes computeBoneAxes() const;
public:
FSManipRotateJoint(LLToolComposite* composite);
virtual ~FSManipRotateJoint() {}
static std::string getManipPartString(EManipPart part);
// Called to designate which joint we are going to manipulate.
void setJoint(LLJoint* joint);
void setAvatar(LLVOAvatar* avatar);
// Overrides
void handleSelect() override;
bool updateVisiblity();
void render() override;
void renderNameXYZ(const LLVector3 &vec);
bool handleMouseDown(S32 x, S32 y, MASK mask) override;
bool handleMouseUp(S32 x, S32 y, MASK mask) override;
bool handleHover(S32 x, S32 y, MASK mask) override;
void drag(S32 x, S32 y) override;
bool isAlwaysRendered() override { return true; }
void highlightManipulators(S32 x, S32 y) override;
bool handleMouseDownOnPart(S32 x, S32 y, MASK mask) override;
void highlightHoverSpheres(S32 mouseX, S32 mouseY);
protected:
// void renderNameXYZ(const std::string name, const LLVector3 &vec);
LLQuaternion dragUnconstrained( S32 x, S32 y );
LLQuaternion dragConstrained( S32 x, S32 y );
LLVector3 getConstraintAxis() const { return mConstraintAxis; };
LLVector3 setConstraintAxis();
bool computeMouseIntersectionOnSphere(S32 x, S32 y,
const LLVector3d& sphere_center_global,
F32 sphere_radius,
LLVector3& outIntersection);
// Instead of selecting an LLViewerObject, we have a single joint
LLJoint* mJoint = nullptr;
BoneAxes mBoneAxes;
LLQuaternion mNaturalAlignmentQuat;
LLVOAvatar* mAvatar = nullptr;
// We'll store the joint's original rotation for reference
LLQuaternion mSavedJointRot;
LLJoint * mHighlightedJoint = nullptr;
F32 mHighlightedPartDistance = 0.f;
LLVector3 mInitialIntersection; // The initial point on the manipulators sphere (in agent space)
const std::vector<std::string_view> getSelectableJoints(){ return sSelectableJoints; };
private:
static const std::vector<std::string_view> sSelectableJoints;
// Structure holding parameters needed to render one manipulator ring.
struct RingRenderParams
{
EManipPart part; // e.g. LL_ROT_Z, LL_ROT_Y, LL_ROT_X, etc.
LLVector4 targetScale; // Target scale for mManipulatorScales for this part.
float extraRotateAngle; // Extra rotation angle (in degrees) to apply.
LLVector3 extraRotateAxis; // Axis for the extra rotation.
LLColor4 primaryColor; // Primary ring color.
LLColor4 secondaryColor; // Secondary ring color.
int scaleIndex; // Which component of mManipulatorScales to use (0: X, 1: Y, 2: Z, 3: Roll).
};
static const std::unordered_map<EManipPart, RingRenderParams> sRingParams;
void updateManipulatorScale(EManipPart part, LLVector4& scales);
void renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color);
void renderManipulatorRings(const LLVector3& center, const LLQuaternion& finalAlignment);
void renderCenterCircle(const F32 radius, const LLColor4 normal_color=LLColor4(0.7f,0.7,0.7f,0.2), const LLColor4 highlight_color=LLColor4(0.8f,0.8f,0.8f,0.3));
void renderCenterSphere(const F32 radius, const LLColor4 normal_color=LLColor4(0.7f,0.7,0.7f,0.2), const LLColor4 highlight_color=LLColor4(0.8f,0.8f,0.8f,0.3));
void renderDetailedRings(float width_meters);
void renderRingPass(const RingRenderParams& params, float radius, float width, int pass);
void renderAxes(const LLVector3& center, F32 size, const LLQuaternion& rotation);
float mLastAngle = 0.f;
LLVector3 mConstraintAxis;
};
#endif // FS_MANIP_ROTATE_JOINT_H

View File

@ -78,7 +78,7 @@ void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoin
posingMotion->removeJointFromState(jointPose);
}
void FSPoserAnimator::resetAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint)
void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -94,27 +94,7 @@ void FSPoserAnimator::resetAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& j
if (!jointPose)
return;
jointPose->setPositionDelta(LLVector3());
jointPose->setRotationDelta(LLQuaternion());
}
void FSPoserAnimator::undoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->undoLastRotationChange();
jointPose->undoLastChange();
if (style == NONE || style == DELTAMODE)
return;
@ -123,10 +103,10 @@ void FSPoserAnimator::undoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastRotationChange();
oppositeJointPose->undoLastChange();
}
void FSPoserAnimator::undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -142,7 +122,9 @@ void FSPoserAnimator::undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return;
jointPose->undoLastPositionChange();
jointPose->setPublicRotation(LLQuaternion());
jointPose->setPublicPosition(LLVector3());
jointPose->setPublicScale(LLVector3());
if (style == NONE || style == DELTAMODE)
return;
@ -151,94 +133,12 @@ void FSPoserAnimator::undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastPositionChange();
oppositeJointPose->setPublicRotation(LLQuaternion());
oppositeJointPose->setPublicPosition(LLVector3());
oppositeJointPose->setPublicScale(LLVector3());
}
void FSPoserAnimator::undoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->undoLastScaleChange();
if (style == NONE || style == DELTAMODE)
return;
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->undoLastScaleChange();
}
void FSPoserAnimator::resetJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setPositionDelta(LLVector3());
if (style == NONE || style == DELTAMODE)
return;
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->setPositionDelta(LLVector3());
}
void FSPoserAnimator::resetJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setScaleDelta(LLVector3());
if (style == NONE || style == DELTAMODE)
return;
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->setScaleDelta(LLVector3());
}
bool FSPoserAnimator::canRedoJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint)
bool FSPoserAnimator::canRedoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint)
{
if (!isAvatarSafeToUse(avatar))
return false;
@ -254,10 +154,10 @@ bool FSPoserAnimator::canRedoJointRotation(LLVOAvatar* avatar, const FSPoserJoin
if (!jointPose)
return false;
return jointPose->canRedoRotation();
return jointPose->canPerformRedo();
}
void FSPoserAnimator::redoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -273,7 +173,7 @@ void FSPoserAnimator::redoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return;
jointPose->redoLastRotationChange();
jointPose->redoLastChange();
if (style == NONE || style == DELTAMODE)
return;
@ -282,63 +182,7 @@ void FSPoserAnimator::redoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastRotationChange();
}
void FSPoserAnimator::redoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->redoLastPositionChange();
if (style == NONE || style == DELTAMODE)
return;
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastPositionChange();
}
void FSPoserAnimator::redoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
if (posingMotion->isStopped())
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->redoLastScaleChange();
if (style == NONE || style == DELTAMODE)
return;
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint.mirrorJointName());
if (!oppositeJointPose)
return;
oppositeJointPose->redoLastScaleChange();
oppositeJointPose->redoLastChange();
}
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
@ -355,7 +199,7 @@ LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return pos;
return jointPose->getPositionDelta();
return jointPose->getPublicPosition();
}
void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style)
@ -377,7 +221,7 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
if (!jointPose)
return;
LLVector3 positionDelta = jointPose->getPositionDelta() - position;
LLVector3 positionDelta = jointPose->getPublicPosition() - position;
switch (style)
{
@ -385,13 +229,13 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
case MIRROR_DELTA:
case SYMPATHETIC_DELTA:
case SYMPATHETIC:
jointPose->setPositionDelta(position);
jointPose->setPublicPosition(position);
break;
case DELTAMODE:
case NONE:
default:
jointPose->setPositionDelta(position);
jointPose->setPublicPosition(position);
return;
}
@ -399,18 +243,18 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
if (!oppositeJointPose)
return;
LLVector3 oppositeJointPosition = oppositeJointPose->getPositionDelta();
LLVector3 oppositeJointPosition = oppositeJointPose->getPublicPosition();
switch (style)
{
case MIRROR:
case MIRROR_DELTA:
oppositeJointPose->setPositionDelta(oppositeJointPosition + positionDelta);
oppositeJointPose->setPublicPosition(oppositeJointPosition + positionDelta);
break;
case SYMPATHETIC_DELTA:
case SYMPATHETIC:
oppositeJointPose->setPositionDelta(oppositeJointPosition - positionDelta);
oppositeJointPose->setPublicPosition(oppositeJointPosition - positionDelta);
break;
default:
@ -478,6 +322,22 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
jointPose->recaptureJoint();
setPosingAvatarJoint(avatar, joint, true);
}
void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->recaptureJointAsDelta();
setPosingAvatarJoint(avatar, joint, true);
}
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const
{
@ -493,7 +353,7 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return vec3;
return translateRotationFromQuaternion(translation, negation, jointPose->getRotationDelta());
return translateRotationFromQuaternion(translation, negation, jointPose->getPublicRotation());
}
void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation,
@ -523,27 +383,27 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case SYMPATHETIC:
case MIRROR:
if (rotationStyle == DELTAIC_ROT)
jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta());
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
else
jointPose->setRotationDelta(absRot);
jointPose->setPublicRotation(absRot);
break;
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta());
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
break;
case DELTAMODE:
jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta());
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
return;
case NONE:
default:
if (rotationStyle == DELTAIC_ROT)
jointPose->setRotationDelta(deltaRot * jointPose->getRotationDelta());
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
else
jointPose->setRotationDelta(absRot);
jointPose->setPublicRotation(absRot);
return;
}
@ -560,7 +420,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
break;
case SYMPATHETIC_DELTA:
oppositeJointPose->setRotationDelta(deltaRot * oppositeJointPose->getRotationDelta());
oppositeJointPose->setPublicRotation(deltaRot * oppositeJointPose->getPublicRotation());
break;
case MIRROR:
@ -569,7 +429,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case MIRROR_DELTA:
inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
oppositeJointPose->setRotationDelta(inv_quat * oppositeJointPose->getRotationDelta());
oppositeJointPose->setPublicRotation(inv_quat * oppositeJointPose->getPublicRotation());
break;
default:
@ -603,6 +463,45 @@ void FSPoserAnimator::reflectJoint(LLVOAvatar* avatar, const FSPoserJoint* joint
}
}
void FSPoserAnimator::symmetrizeLeftToRightOrRightToLeft(LLVOAvatar* avatar, bool rightToLeft)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
for (size_t index = 0; index != PoserJoints.size(); ++index)
{
if (!PoserJoints[index].dontFlipOnMirror())
continue;
bool currentlyPosing = isPosingAvatarJoint(avatar, PoserJoints[index]);
if (!currentlyPosing)
continue;
auto oppositeJoint = getPoserJointByName(PoserJoints[index].mirrorJointName());
if (!oppositeJoint)
continue;
bool currentlyPosingOppositeJoint = isPosingAvatarJoint(avatar, *oppositeJoint);
if (!currentlyPosingOppositeJoint)
continue;
FSJointPose* rightJointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
FSJointPose* leftJointPose = posingMotion->getJointPoseByJointName(oppositeJoint->jointName());
if (!leftJointPose || !rightJointPose)
return;
if (rightToLeft)
leftJointPose->mirrorRotationFrom(rightJointPose);
else
rightJointPose->mirrorRotationFrom(leftJointPose);
}
}
void FSPoserAnimator::flipEntirePose(LLVOAvatar* avatar)
{
if (!isAvatarSafeToUse(avatar))
@ -750,7 +649,7 @@ LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint&
if (!jointPose)
return scale;
return jointPose->getScaleDelta();
return jointPose->getPublicScale();
}
void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style)
@ -772,7 +671,7 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
if (!jointPose)
return;
jointPose->setScaleDelta(scale);
jointPose->setPublicScale(scale);
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
@ -783,7 +682,7 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
case MIRROR:
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
oppositeJointPose->setScaleDelta(scale);
oppositeJointPose->setPublicScale(scale);
break;
case DELTAMODE:
@ -810,10 +709,10 @@ bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJo
if (!jointPose)
return false;
LLQuaternion rotationDelta = jointPose->getRotationDelta();
LLQuaternion rotationDelta = jointPose->getPublicRotation();
rotationDelta.getEulerAngles(&rot->mV[VX], &rot->mV[VY], &rot->mV[VZ]);
pos->set(jointPose->getPositionDelta());
scale->set(jointPose->getScaleDelta());
pos->set(jointPose->getPublicPosition());
scale->set(jointPose->getPublicScale());
*baseRotationIsZero = jointPose->isBaseRotationZero();
return true;
@ -836,7 +735,7 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
jointPose->zeroBaseRotation();
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setRotationDelta(rot);
jointPose->setPublicRotation(rot);
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
@ -853,9 +752,9 @@ void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint*
return;
if (loadPositionAsDelta)
jointPose->setPositionDelta(position);
jointPose->setPublicPosition(position);
else
jointPose->setPositionDelta(position);
jointPose->setPublicPosition(position);
}
void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale)
@ -872,9 +771,9 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
return;
if (loadScaleAsDelta)
jointPose->setScaleDelta(scale);
jointPose->setPublicScale(scale);
else
jointPose->setScaleDelta(scale);
jointPose->setPublicScale(scale);
}
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName)

View File

@ -398,46 +398,19 @@ public:
void setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, bool shouldPose);
/// <summary>
/// Resets the supplied PoserJoint to its position/rotation/scale it was when poser was started.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint to be reset.</param>
void resetAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint);
/// <summary>
/// Undoes the last applied rotation to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the rotation to undo.</param>
void undoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Undoes the last applied position to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the position to undo.</param>
void undoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Undoes the last applied scale to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the scale to undo.</param>
void undoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Resets the position of the supplied PoserJoint.
/// Resets the supplied PoserJoint to the position it had when poser was started.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the position to reset.</param>
void resetJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <param name="style">The style to apply the reset with; if a style that support more than one joint, more that one joint will be reset.</param>
void resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Resets the scale of the supplied joint to initial values.
/// Undoes the last applied change (rotation, position or scale) to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the scale to reset.</param>
void resetJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <param name="joint">The joint with the rotation to undo.</param>
void undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Determines if a redo action is currently permitted for the supplied joint.
@ -445,28 +418,14 @@ public:
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint to query.</param>
/// <returns>True if a redo action is available, otherwise false.</returns>
bool canRedoJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint);
bool canRedoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint);
/// <summary>
/// Re-does the last undone rotation to the supplied PoserJoint.
/// Re-does the last undone change (rotation, position or scale) to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the rotation to redo.</param>
void redoLastJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Re-does the last undone position to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the position to redo.</param>
void redoLastJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Re-does the last undone scale to the supplied PoserJoint.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint with the scale to redo.</param>
void redoLastJointScale(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
void redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style);
/// <summary>
/// Gets the position of a joint for the supplied avatar.
@ -541,14 +500,23 @@ public:
/// <param name="avatar">The avatar whose pose should flip left-right.</param>
void flipEntirePose(LLVOAvatar* avatar);
/// <summary>
/// Symmetrizes the rotations of the joints from one side of the supplied avatar to the other.
/// </summary>
/// <param name="avatar">The avatar whose joints to symmetrizet.</param>
/// <param name="rightToLeft">Whether to symmetrize rotations from right to left, otherwise symmetrize left to right.</param>
void symmetrizeLeftToRightOrRightToLeft(LLVOAvatar* avatar, bool rightToLeft);
/// <summary>
/// Recaptures the rotation, position and scale state of the supplied joint for the supplied avatar.
/// AsDelta variant retians the original base and creates a delta relative to it.
/// </summary>
/// <param name="avatar">The avatar whose joint is to be recaptured.</param>
/// <param name="joint">The joint to recapture.</param>
/// <param name="translation">The axial translation form the supplied joint.</param>
/// <param name="negation">The style of negation to apply to the recapture.</param>
void recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation);
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation);
/// <summary>
/// Sets all of the joint rotations of the supplied avatar to zero.

View File

@ -260,7 +260,7 @@ void FSPosingMotion::setAllRotationsToZero()
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
poserJoint_iter->zeroBaseRotation();
poserJoint_iter->setRotationDelta(LLQuaternion::DEFAULT);
poserJoint_iter->setPublicRotation(LLQuaternion::DEFAULT);
}
}

View File

@ -771,7 +771,7 @@ LLAppViewer::LLAppViewer()
// static_debug_info.log file
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
// <FS:Beq> Improve Bugsplat tracking by using attributes
BugSplatAttributes::setCrashContextFileName(logdir + "crash-context.xml");
BugSplatAttributes::setCrashContextFileName(logdir + "CrashContext.xml");
// </FS:Beq>
# else // ! LL_BUGSPLAT
// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.

View File

@ -40,6 +40,7 @@ class LLColor4;
class LLManipRotate : public LLManip
{
friend class FSManipRotateJoint;
public:
class ManipulatorHandle
{
@ -67,13 +68,13 @@ public:
private:
void updateHoverView();
void drag( S32 x, S32 y );
virtual void drag( S32 x, S32 y );
LLVector3 projectToSphere( F32 x, F32 y, bool* on_sphere );
void renderSnapGuides();
void renderActiveRing(F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color);
bool updateVisiblity();
virtual bool updateVisiblity();
LLVector3 findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis );
LLQuaternion dragUnconstrained( S32 x, S32 y );

View File

@ -899,3 +899,165 @@ bool LLToolCompGun::handleScrollWheel(S32 x, S32 y, S32 clicks)
}
return true;
}
#include "llviewerwindow.h" // for gViewerWindow->pickAsync()
#include "llselectmgr.h" // for LLSelectMgr
#include "llfloaterreg.h" // for LLFloaterReg::showInstance()
#include "llviewermenu.h" // for LLEditMenuHandler::gEditMenuHandler
#include "fsfloaterposer.h"
// If you want a standard static instance approach:
FSToolCompPose* FSToolCompPose::getInstance()
{
// Meyers singleton pattern
static FSToolCompPose instance;
return &instance;
}
//-----------------------------------
// Constructor
FSToolCompPose::FSToolCompPose()
: LLToolComposite(std::string("Pose"))
{
// Create a joint manipulator
mManip = new FSManipRotateJoint(this);
// Possibly create a selection rectangle tool if you want
// to be able to box-select joints or objects
// (same usage as LLToolCompRotate does)
// mSelectRect = new LLToolSelectRect(this);
// Set the default and current subtool
mCur = mManip;
mDefault = mManip;
}
//-----------------------------------
// Destructor
FSToolCompPose::~FSToolCompPose()
{
delete mManip;
mManip = nullptr;
delete mSelectRect;
mSelectRect = nullptr;
}
//-----------------------------------
// Handle Hover
bool FSToolCompPose::handleHover(S32 x, S32 y, MASK mask)
{
// If the current subtool hasn't captured the mouse,
// switch to your manip subtool (like LLToolCompRotate).
if (!mCur->hasMouseCapture())
{
setCurrentTool(mManip);
}
return mCur->handleHover(x, y, mask);
}
//-----------------------------------
// Handle MouseDown
bool FSToolCompPose::handleMouseDown(S32 x, S32 y, MASK mask)
{
mMouseDown = true;
// Kick off an async pick, which calls pickCallback when complete
// so we can see if user clicked on a manip ring or not
gViewerWindow->pickAsync(x, y, mask, pickCallback);
return true;
}
//-----------------------------------
// The pickCallback
void FSToolCompPose::pickCallback(const LLPickInfo& pick_info)
{
FSToolCompPose* self = FSToolCompPose::getInstance();
FSManipRotateJoint* manip = self->mManip;
if (!manip) return; // No manipulator available, exit
// Highlight the manipulator based on the mouse position
manip->highlightManipulators(pick_info.mMousePt.mX, pick_info.mMousePt.mY);
if (!self->mMouseDown)
{
// No action needed if mouse is up; interaction is handled by highlight logic
return;
}
// Check if a manipulator ring is highlighted
if (manip->getHighlightedPart() != LLManip::LL_NO_PART)
{
// Switch to the manipulator tool for dragging
self->setCurrentTool(manip);
manip->handleMouseDownOnPart(pick_info.mMousePt.mX, pick_info.mMousePt.mY, pick_info.mKeyMask);
}
else
{
// If no ring is highlighted, reset interaction or do nothing
LL_DEBUGS("FSToolCompPose") << "No manipulator ring selected" << LL_ENDL;
}
}
//-----------------------------------
// Handle MouseUp
bool FSToolCompPose::handleMouseUp(S32 x, S32 y, MASK mask)
{
mMouseDown = false;
// The base LLToolComposite sets mCur->handleMouseUp(...)
// and does other management
return LLToolComposite::handleMouseUp(x, y, mask);
}
//-----------------------------------
// getOverrideTool
// If you want SHIFT+CTRL combos to do something else
LLTool* FSToolCompPose::getOverrideTool(MASK mask)
{
// Example from LLToolCompRotate that calls scale if SHIFT+CTRL
if (mask == (MASK_CONTROL | MASK_SHIFT))
{
// If you have a scale tool, return that. Or else remove
// this if you don't want an override.
return LLToolCompScale::getInstance();
}
// Otherwise fallback
return LLToolComposite::getOverrideTool(mask);
}
//-----------------------------------
// Handle DoubleClick
bool FSToolCompPose::handleDoubleClick(S32 x, S32 y, MASK mask)
{
if (!mManip->getSelection()->isEmpty() &&
mManip->getHighlightedPart() == LLManip::LL_NO_PART)
{
// Possibly show some pose properties or open the pose floater
mPoser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::showInstance("fs_poser"));
return true;
}
else
{
// If nothing selected, try a mouse down again
return handleMouseDown(x, y, mask);
}
}
//-----------------------------------
// render
void FSToolCompPose::render()
{
// Render the current subtool
mCur->render();
// If the current subtool is not the manip, we can still
// optionally draw manip guidelines in the background
if (mCur != mManip)
{
mManip->renderGuidelines(); // or something similar if your manip has it
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
}
}

View File

@ -252,5 +252,44 @@ protected:
LLTool* mNull;
};
// Subclass of LLToolComposite
#include "fsmaniprotatejoint.h" // For FSManipRotateJoint
#include "fsfloaterposer.h"
class FSToolCompPose : public LLToolComposite
{
public:
// Typical pattern: pass a name like "Pose"
FSToolCompPose();
virtual ~FSToolCompPose();
// For some viewer patterns, we create a static singleton:
static FSToolCompPose* getInstance();
// Overriding base events:
virtual bool handleHover(S32 x, S32 y, MASK mask) override;
virtual bool handleMouseDown(S32 x, S32 y, MASK mask) override;
virtual bool handleMouseUp(S32 x, S32 y, MASK mask) override;
virtual bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
virtual void render() override;
void setAvatar(LLVOAvatar* avatar) { mManip->setAvatar(avatar); };
void setJoint( LLJoint * joint ) { mManip->setJoint( joint ); };
// Optional override if you have SHIFT/CTRL combos
virtual LLTool* getOverrideTool(MASK mask) override;
// The pick callback invoked on async pick
static void pickCallback(const LLPickInfo& pick_info);
void setPoserFloater(FSFloaterPoser* poser){ mPoser = poser; };
FSFloaterPoser* getPoserFloater(){ return mPoser; };
protected:
// Tools within this composite
FSManipRotateJoint* mManip = nullptr;
LLToolSelectRect* mSelectRect= nullptr;
FSFloaterPoser* mPoser = nullptr;
// Track mouse state similarly to LLToolCompRotate
bool mMouseDown = false;
};
#endif // LL_TOOLCOMP_H

View File

@ -70,6 +70,7 @@ LLToolset* gCameraToolset = NULL;
//LLToolset* gLandToolset = NULL;
LLToolset* gMouselookToolset = NULL;
LLToolset* gFaceEditToolset = NULL;
LLToolset* gPoserToolset = nullptr;
/////////////////////////////////////////////////////
// LLToolMgr
@ -99,6 +100,8 @@ LLToolMgr::LLToolMgr()
// gLandToolset = new LLToolset();
gMouselookToolset = new LLToolset();
gFaceEditToolset = new LLToolset();
gPoserToolset = new LLToolset();
gPoserToolset->setShowFloaterTools(false);
gMouselookToolset->setShowFloaterTools(false);
gFaceEditToolset->setShowFloaterTools(false);
}
@ -121,6 +124,7 @@ void LLToolMgr::initTools()
gMouselookToolset->addTool( LLToolCompGun::getInstance() );
gBasicToolset->addTool( LLToolCompInspect::getInstance() );
gFaceEditToolset->addTool( LLToolCamera::getInstance() );
gPoserToolset->addTool( FSToolCompPose::getInstance() );
// On startup, use "select" tool
setCurrentToolset(gBasicToolset);

View File

@ -129,6 +129,7 @@ extern LLToolset *gCameraToolset;
//extern LLToolset *gLandToolset;
extern LLToolset* gMouselookToolset;
extern LLToolset* gFaceEditToolset;
extern LLToolset* gPoserToolset;
extern LLTool* gToolNull;

View File

@ -5096,6 +5096,13 @@ void LLViewerWindow::renderSelections( bool for_gl_pick, bool pick_parcel_walls,
// Call this once and only once
LLSelectMgr::getInstance()->updateSilhouettes();
}
// <FS:Beq> render the poser manipulator guides
// if we have something selected those toosl should override
if ( (!for_hud) && (selection->isEmpty()) && (LLToolMgr::getInstance()->getCurrentTool() == FSToolCompPose::getInstance()) )
{
FSToolCompPose::getInstance()->render();
}
// </FS:Beq>
// Draw fence around land selections
if (for_gl_pick)

View File

@ -1934,6 +1934,227 @@ void LLVOAvatar::renderBones(const std::string &selected_joint)
}
}
void LLVOAvatar::renderOnlySelectedBones(const std::vector<std::string> &selected_joints)
{
LLGLEnable blend(GL_BLEND);
avatar_joint_list_t::iterator iter = mSkeleton.begin();
avatar_joint_list_t::iterator end = mSkeleton.end();
// For selected joints
static LLVector3 SELECTED_COLOR_OCCLUDED(1.0f, 1.0f, 0.0f);
static LLVector3 SELECTED_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
// For bones with position overrides defined
static LLVector3 OVERRIDE_COLOR_OCCLUDED(1.0f, 0.0f, 0.0f);
static LLVector3 OVERRIDE_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
// For bones which are rigged to by at least one attachment
static LLVector3 RIGGED_COLOR_OCCLUDED(0.0f, 1.0f, 1.0f);
static LLVector3 RIGGED_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
// For bones not otherwise colored
static LLVector3 OTHER_COLOR_OCCLUDED(0.0f, 1.0f, 0.0f);
static LLVector3 OTHER_COLOR_VISIBLE(0.5f, 0.5f, 0.5f);
static F32 SPHERE_SCALEF = 0.001f;
for (; iter != end; ++iter)
{
LLJoint* jointp = *iter;
if (!jointp)
{
continue;
}
if (std::find(selected_joints.begin(), selected_joints.end(), jointp->getName()) == selected_joints.end())
{
continue;
}
jointp->updateWorldMatrix();
LLVector3 occ_color, visible_color;
occ_color = SELECTED_COLOR_OCCLUDED;
visible_color = SELECTED_COLOR_VISIBLE;
gGL.pushMatrix();
gGL.multMatrix( &jointp->getXform()->getWorldMatrix().mMatrix[0][0] );
gGL.diffuseColor3f( 1.f, 0.f, 1.f );
gGL.begin(LLRender::LINES);
LLVector3 v[] =
{
LLVector3(1,0,0),
LLVector3(-1,0,0),
LLVector3(0,1,0),
LLVector3(0,-1,0),
LLVector3(0,0,-1),
LLVector3(0,0,1),
};
//sides
gGL.vertex3fv(v[0].mV);
gGL.vertex3fv(v[2].mV);
gGL.vertex3fv(v[0].mV);
gGL.vertex3fv(v[3].mV);
gGL.vertex3fv(v[1].mV);
gGL.vertex3fv(v[2].mV);
gGL.vertex3fv(v[1].mV);
gGL.vertex3fv(v[3].mV);
//top
gGL.vertex3fv(v[0].mV);
gGL.vertex3fv(v[4].mV);
gGL.vertex3fv(v[1].mV);
gGL.vertex3fv(v[4].mV);
gGL.vertex3fv(v[2].mV);
gGL.vertex3fv(v[4].mV);
gGL.vertex3fv(v[3].mV);
gGL.vertex3fv(v[4].mV);
//bottom
gGL.vertex3fv(v[0].mV);
gGL.vertex3fv(v[5].mV);
gGL.vertex3fv(v[1].mV);
gGL.vertex3fv(v[5].mV);
gGL.vertex3fv(v[2].mV);
gGL.vertex3fv(v[5].mV);
gGL.vertex3fv(v[3].mV);
gGL.vertex3fv(v[5].mV);
gGL.end();
gGL.popMatrix();
// renderBoxAroundJointAttachments( jointp );
}
// // draw joint space bounding boxes of rigged attachments in yellow
// gGL.color3f(1.f, 1.f, 0.f);
// for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
// {
// LLJoint* joint = getJoint(joint_num);
// LLJointRiggingInfo* rig_info = NULL;
// if (joint_num < mJointRiggingInfoTab.size())
// {
// rig_info = &mJointRiggingInfoTab[joint_num];
// }
// if (joint && rig_info && rig_info->isRiggedTo())
// {
// LLViewerJointAttachment* as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint);
// if (as_joint_attach && as_joint_attach->getIsHUDAttachment())
// {
// // Ignore bounding box of HUD joints
// continue;
// }
// gGL.pushMatrix();
// gGL.multMatrix(&joint->getXform()->getWorldMatrix().mMatrix[0][0]);
// LLVector4a pos;
// LLVector4a size;
// const LLVector4a* extents = rig_info->getRiggedExtents();
// pos.setAdd(extents[0], extents[1]);
// pos.mul(0.5f);
// size.setSub(extents[1], extents[0]);
// size.mul(0.5f);
// drawBoxOutline(pos, size);
// gGL.popMatrix();
// }
// }
// draw world space attachment rigged bounding boxes in cyan
// gGL.color3f(0.f, 1.f, 1.f);
// for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
// iter != mAttachmentPoints.end();
// ++iter)
// {
// LLViewerJointAttachment* attachment = iter->second;
// if (attachment->getValid())
// {
// for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
// attachment_iter != attachment->mAttachedObjects.end();
// ++attachment_iter)
// {
// LLViewerObject* attached_object = attachment_iter->get();
// if (attached_object && !attached_object->isHUDAttachment())
// {
// LLDrawable* drawable = attached_object->mDrawable;
// if (drawable && drawable->isState(LLDrawable::RIGGED | LLDrawable::RIGGED_CHILD))
// {
// // get face rigged extents
// for (S32 i = 0; i < drawable->getNumFaces(); ++i)
// {
// LLFace* facep = drawable->getFace(i);
// if (facep && facep->isState(LLFace::RIGGED))
// {
// LLVector4a center, size;
// LLVector4a* extents = facep->mRiggedExtents;
// center.setAdd(extents[0], extents[1]);
// center.mul(0.5f);
// size.setSub(extents[1], extents[0]);
// size.mul(0.5f);
// drawBoxOutline(center, size);
// }
// }
// }
// }
// }
// }
// }
}
void LLVOAvatar::renderBoxAroundJointAttachments(LLJoint * joint)
{
LLJointRiggingInfo* rig_info = NULL;
if (joint->getJointNum() < mJointRiggingInfoTab.size())
{
rig_info = &mJointRiggingInfoTab[joint->getJointNum()];
}
if (joint && rig_info && rig_info->isRiggedTo())
{
LLViewerJointAttachment* as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint);
gGL.pushMatrix();
gGL.multMatrix(&joint->getXform()->getWorldMatrix().mMatrix[0][0]);
LLVector4a pos;
LLVector4a size;
const LLVector4a* extents = rig_info->getRiggedExtents();
pos.setAdd(extents[0], extents[1]);
pos.mul(0.5f);
size.setSub(extents[1], extents[0]);
size.mul(0.5f);
drawBoxOutline(pos, size);
gGL.popMatrix();
}
}
void LLVOAvatar::renderJoints()
{

View File

@ -568,6 +568,9 @@ public:
U32 renderTransparent(bool first_pass);
void renderCollisionVolumes();
void renderBones(const std::string &selected_joint = std::string());
void renderOnlySelectedBones(const std::vector<std::string> &selected_joints);
void renderBoxAroundJointAttachments(LLJoint * joint);
void renderJoints();
static void deleteCachedImages(bool clearAll=true);
static void destroyGL();

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -593,6 +593,11 @@ with the same filename but different name
<texture name="Pathfinding_Dirty_Light" file_name="icons/Pathfinding_Dirty_Light.png" preload="false" />
<texture name="Pathfinding_Disabled_Light" file_name="icons/Pathfinding_Disabled_Light.png" preload="false" />
<!-- FS:Beq: Poser icons -->
<texture name="Poser_Visual_On" file_name="icons/visual_pose_enabled.png" />
<texture name="Poser_Visual_Off" file_name="icons/visual_pose_disabled.png" />
<!-- FS:Ansariel: Icon for script errors in V1 status bar -->
<texture name="Statusbar_Script_Error" file_name="icons/status_script_debug.tga" preload="false" />

File diff suppressed because it is too large Load Diff