Poser: Update diff save, Rotation frames, minor enhancements
Signed-off-by: Angeldark Raymaker <dark.angel_raven@yahoo.com.au> No collab poser Manip: Integrates better with Poser, now working 'live' like all the other UI controls, feeding back in the same way Manip: code tidy Rotation framing: World/Avatar/Screen reference framing for manip & other UI elements Bone hightlight: with manip off, a debug marker appears a second to guide the eye as you select bones Diff saves: Better saving of pose-statemaster
parent
4cb05bb55e
commit
08f8af3cb2
|
|
@ -8177,6 +8177,17 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSPoserShowBoneHighlights</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Whether to highlight a bone with the debug beacon on selection from the UI.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>FSPoserStopPosingWhenClosed</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include "llinventoryfunctions.h"
|
||||
#include "lltoolcomp.h"
|
||||
#include "llloadingindicator.h"
|
||||
#include "llmutelist.h"
|
||||
#include "llappviewer.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -57,6 +59,7 @@ constexpr std::string_view POSE_PRESETS_HANDS_SUBDIRECTORY = "hand_presets";
|
|||
constexpr char XML_LIST_HEADER_STRING_PREFIX[] = "header_";
|
||||
constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_";
|
||||
constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_";
|
||||
constexpr char XML_JOINT_FRAME_TRANSFORM_PREFIX[] = "joint_frame_";
|
||||
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_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
|
||||
|
|
@ -64,6 +67,7 @@ constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPos
|
|||
constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso";
|
||||
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
|
||||
constexpr std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave";
|
||||
constexpr std::string_view POSER_SHOWBONEHIGHLIGHTS_SAVE_KEY = "FSManipShowJointMarkers";
|
||||
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
|
||||
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
|
||||
|
||||
|
|
@ -82,6 +86,7 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
|
|||
mCommitCallbackRegistrar.add("Poser.StartStopAnimating", [this](LLUICtrl*, const LLSD&) { onPoseStartStop(); });
|
||||
mCommitCallbackRegistrar.add("Poser.ToggleLoadSavePanel", [this](LLUICtrl*, const LLSD&) { onToggleLoadSavePanel(); });
|
||||
mCommitCallbackRegistrar.add("Poser.ToggleVisualManipulators", [this](LLUICtrl*, const LLSD&) { onToggleVisualManipulators(); });
|
||||
mCommitCallbackRegistrar.add("Poser.ToggleRotationFrame", [this](LLUICtrl* button, const LLSD&) { onToggleRotationFrameButton(button); });
|
||||
|
||||
mCommitCallbackRegistrar.add("Poser.UndoLastRotation", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); });
|
||||
mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); });
|
||||
|
|
@ -205,9 +210,17 @@ bool FSFloaterPoser::postBuild()
|
|||
mRedoChangeBtn = getChild<LLButton>("button_redo_change");
|
||||
mUndoChangeBtn = getChild<LLButton>("undo_change");
|
||||
mSetToTposeButton = getChild<LLButton>("set_t_pose_button");
|
||||
mBtnJointReset = getChild<LLButton>("poser_joint_reset");
|
||||
|
||||
mBtnWorldFrame = getChild<LLButton>("poser_world_frame_toggle");
|
||||
mBtnAvatarFrame = getChild<LLButton>("poser_avatar_frame_toggle");
|
||||
mBtnScreenFrame = getChild<LLButton>("poser_screen_frame_toggle");
|
||||
|
||||
mJointsParentPnl = getChild<LLPanel>("joints_parent_panel");
|
||||
mTrackballPnl = getChild<LLPanel>("trackball_panel");
|
||||
mPositionPnl = getChild<LLPanel>("position_panel");
|
||||
mMoveTabPnl = getChild<LLPanel>("move_tab_panel");
|
||||
mTrackballButtonPnl = getChild<LLPanel>("trackball_button_panel");
|
||||
mPositionRotationPnl = getChild<LLPanel>("positionRotation_panel");
|
||||
mBodyJointsPnl = getChild<LLPanel>("body_joints_panel");
|
||||
mFaceJointsPnl = getChild<LLPanel>("face_joints_panel");
|
||||
|
|
@ -269,6 +282,76 @@ void FSFloaterPoser::onFocusLost()
|
|||
}
|
||||
}
|
||||
|
||||
void FSFloaterPoser::draw()
|
||||
{
|
||||
LLFloater::draw();
|
||||
|
||||
drawOnHoverJointHint();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::markSelectedJointsToHighlight()
|
||||
{
|
||||
bool toolsEnabled = mToggleVisualManipulators->getValue().asBoolean();
|
||||
if (toolsEnabled)
|
||||
return;
|
||||
|
||||
bool showHighlights = gSavedSettings.getBOOL(POSER_SHOWBONEHIGHLIGHTS_SAVE_KEY);
|
||||
if (!showHighlights)
|
||||
return;
|
||||
|
||||
auto selectedJoints = getUiSelectedPoserJoints();
|
||||
if (selectedJoints.size() < 1)
|
||||
return;
|
||||
|
||||
std::string jointName = selectedJoints[0]->jointName();
|
||||
bool isRightLimb = jointName.find("Right") != std::string::npos;
|
||||
bool isLeftLimb = jointName.find("Left") != std::string::npos;
|
||||
|
||||
if (!(isRightLimb || isLeftLimb))
|
||||
return;
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
mLastSelectedJoint = selectedJoints[0];
|
||||
timeFadeStartedMicrosec = gFrameTime;
|
||||
}
|
||||
|
||||
void FSFloaterPoser::drawOnHoverJointHint()
|
||||
{
|
||||
if (!mLastSelectedJoint)
|
||||
return;
|
||||
|
||||
constexpr U64 GLOW_TIME_US = 300000;
|
||||
U64 fadeTimeUs = gFrameTime - timeFadeStartedMicrosec;
|
||||
if (fadeTimeUs > GLOW_TIME_US)
|
||||
return;
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
LLJoint* joint = avatar->getJoint(std::string(mLastSelectedJoint->jointName()));
|
||||
if (!joint)
|
||||
return;
|
||||
|
||||
F32 alphaFade = 1.f * (GLOW_TIME_US - fadeTimeUs) / GLOW_TIME_US;
|
||||
static LLUIColor mBeaconColor = LLUIColorTable::getInstance()->getColor("AreaSearchBeaconColor");
|
||||
LLColor4 beaconColour = mBeaconColor.get();
|
||||
beaconColour.setAlpha(alphaFade);
|
||||
LLVector3 joint_world_position = joint->getWorldPosition();
|
||||
|
||||
static LLCachedControl<S32> beacon_line_width(gSavedSettings, "DebugBeaconLineWidth");
|
||||
gObjectList.addDebugBeacon(joint_world_position, "", beaconColour, beaconColour, beacon_line_width);
|
||||
}
|
||||
|
||||
void FSFloaterPoser::enableVisualManipulators()
|
||||
{
|
||||
if (!gAgentAvatarp || gAgentAvatarp.isNull())
|
||||
|
|
@ -359,6 +442,9 @@ void FSFloaterPoser::onPoseFileSelect()
|
|||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
|
||||
return;
|
||||
|
||||
bool enableButtons = mPoserAnimator.isPosingAvatar(avatar);
|
||||
mLoadPosesBtn->setEnabled(enableButtons);
|
||||
mSavePosesBtn->setEnabled(enableButtons);
|
||||
|
|
@ -409,11 +495,14 @@ void FSFloaterPoser::onClickPoseSave()
|
|||
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
|
||||
{
|
||||
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
|
||||
return;
|
||||
}
|
||||
|
||||
// if prompts are disabled or file doesn't exist, do the save immediately:
|
||||
const bool prompt = gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY);
|
||||
|
||||
|
|
@ -534,7 +623,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
record["startFromTeePose"]["value"] = !savingDiff;
|
||||
|
||||
if (savingDiff)
|
||||
mPoserAnimator.savePosingState(avatar, &record);
|
||||
mPoserAnimator.savePosingState(avatar, false, &record);
|
||||
|
||||
LLVector3 rotation, position, scale, zeroVector;
|
||||
bool baseRotationIsZero;
|
||||
|
|
@ -694,17 +783,19 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
|
|||
if (currentlyPosing)
|
||||
continue;
|
||||
|
||||
mPoserAnimator.recaptureJoint(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName()));
|
||||
mPoserAnimator.recaptureJoint(avatar, *item);
|
||||
}
|
||||
|
||||
setSavePosesButtonText(true);
|
||||
refreshRotationSlidersAndSpinners();
|
||||
refreshPositionSlidersAndSpinners();
|
||||
refreshScaleSlidersAndSpinners();
|
||||
refreshTrackpadCursor();
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::updatePosedBones(const std::string& jointName)
|
||||
void FSFloaterPoser::updatePosedBones(const std::string& jointName, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale)
|
||||
{
|
||||
LLVOAvatar *avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
|
|
@ -713,12 +804,19 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName)
|
|||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
bool haveImplicitPermission = havePermissionToAnimateAvatar(avatar); // self & control avatars you own
|
||||
bool iCanPoseThem = havePermissionToAnimateOtherAvatar(avatar);
|
||||
if (!haveImplicitPermission && !iCanPoseThem)
|
||||
return;
|
||||
|
||||
const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName);
|
||||
if (!poserJoint)
|
||||
return;
|
||||
|
||||
bool savingToExternal = getSavingToBvh();
|
||||
mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, savingToExternal, getUiSelectedBoneDeflectionStyle());
|
||||
bool savingToExternal = getSavingToBvh();
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
mPoserAnimator.updateJointFromManip(avatar, poserJoint, savingToExternal, defl, frame, rotation, position, scale);
|
||||
|
||||
refreshRotationSlidersAndSpinners();
|
||||
refreshPositionSlidersAndSpinners();
|
||||
|
|
@ -728,6 +826,24 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName)
|
|||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
LLQuaternion FSFloaterPoser::getManipGimbalRotation(const std::string& jointName)
|
||||
{
|
||||
LLVOAvatar *avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return LLQuaternion();
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return LLQuaternion();
|
||||
|
||||
const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName);
|
||||
if (!poserJoint)
|
||||
return LLQuaternion();
|
||||
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
|
||||
return mPoserAnimator.getManipGimbalRotation(avatar, poserJoint, frame);
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onClickBrowsePoseCache()
|
||||
{
|
||||
createUserPoseDirectoryIfNeeded();
|
||||
|
|
@ -930,6 +1046,9 @@ void FSFloaterPoser::timedReload()
|
|||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
|
||||
return;
|
||||
|
||||
if (loadPoseFromXml(avatar, mLoadPoseTimer->getPosePath(), mLoadPoseTimer->getLoadMethod()))
|
||||
{
|
||||
mLoadPoseTimer->completeLoading();
|
||||
|
|
@ -1024,6 +1143,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
|
|||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
|
||||
return;
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
|
|
@ -1233,8 +1354,8 @@ bool FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
mPoserAnimator.setRotationIsMirrored(avatar, *poserJoint, mirroredJoint);
|
||||
}
|
||||
|
||||
if (version > 6 && !startFromZeroRot)
|
||||
loadSuccess = mPoserAnimator.loadPosingState(avatar, pose);
|
||||
if (version > 6 && !startFromZeroRot && !loadSelective)
|
||||
loadSuccess = mPoserAnimator.loadPosingState(avatar, true, pose);
|
||||
}
|
||||
}
|
||||
catch ( const std::exception & e )
|
||||
|
|
@ -1326,8 +1447,25 @@ bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar *avatar) const
|
|||
return false;
|
||||
if (avatar->isSelf())
|
||||
return true;
|
||||
|
||||
if (avatar->isControlAvatar())
|
||||
return true;
|
||||
{
|
||||
LLControlAvatar* control_av = dynamic_cast<LLControlAvatar*>(avatar);
|
||||
const LLVOVolume* rootVolume = control_av->mRootVolp;
|
||||
const LLViewerObject* rootEditObject = (rootVolume) ? rootVolume->getRootEdit() : NULL;
|
||||
if (!rootEditObject)
|
||||
return false;
|
||||
|
||||
return rootEditObject->permYouOwner();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FSFloaterPoser::havePermissionToAnimateOtherAvatar(LLVOAvatar *avatar) const
|
||||
{
|
||||
if (!avatar || avatar->isDead())
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1342,6 +1480,12 @@ void FSFloaterPoser::poseControlsEnable(bool enable)
|
|||
mLoadPosesBtn->setEnabled(enable);
|
||||
mSavePosesBtn->setEnabled(enable);
|
||||
mPoseSaveNameEditor->setEnabled(enable);
|
||||
mBtnJointReset->setEnabled(enable);
|
||||
mRedoChangeBtn->setEnabled(enable);
|
||||
mUndoChangeBtn->setEnabled(enable);
|
||||
mPositionPnl->setEnabled(enable);
|
||||
mMoveTabPnl->setEnabled(enable);
|
||||
mTrackballButtonPnl->setEnabled(enable);
|
||||
}
|
||||
|
||||
void FSFloaterPoser::refreshJointScrollListMembers()
|
||||
|
|
@ -1500,6 +1644,23 @@ void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool toggling
|
|||
refreshTrackpadCursor();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onToggleRotationFrameButton(const LLUICtrl* toggleButton)
|
||||
{
|
||||
if (!toggleButton)
|
||||
return;
|
||||
|
||||
bool buttonDown = toggleButton->getValue().asBoolean();
|
||||
if (buttonDown)
|
||||
{
|
||||
mBtnAvatarFrame->setValue(toggleButton == mBtnAvatarFrame);
|
||||
mBtnScreenFrame->setValue(toggleButton == mBtnScreenFrame);
|
||||
mBtnWorldFrame->setValue(toggleButton == mBtnWorldFrame);
|
||||
}
|
||||
|
||||
FSToolCompPose::getInstance()->setReferenceFrame(getReferenceFrame());
|
||||
refreshRotationSlidersAndSpinners();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onUndoLastChange()
|
||||
{
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
|
|
@ -1733,7 +1894,6 @@ LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel * tabPanel) const
|
|||
return mCollisionVolumesScrollList;
|
||||
}
|
||||
|
||||
LL_WARNS() << "Unknown tab panel: " << tabPanel << LL_ENDL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -1787,7 +1947,10 @@ void FSFloaterPoser::updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimat
|
|||
if (!avatarp)
|
||||
return;
|
||||
|
||||
if (joints.size() >= 1)
|
||||
bool haveImplicitPermission = havePermissionToAnimateAvatar(avatarp);
|
||||
bool iCanPoseThem = havePermissionToAnimateOtherAvatar(avatarp);
|
||||
|
||||
if ((joints.size() >= 1) && (haveImplicitPermission || iCanPoseThem))
|
||||
FSToolCompPose::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName()));
|
||||
else
|
||||
FSToolCompPose::getInstance()->setJoint(nullptr);
|
||||
|
|
@ -2073,8 +2236,9 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
|
|||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
LLVector3 vec3 = LLVector3(x, y, z);
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
LLVector3 vec3 = LLVector3(x, y, z);
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
|
||||
for (auto item : getUiSelectedPoserJoints())
|
||||
{
|
||||
|
|
@ -2082,7 +2246,7 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
|
|||
if (!currentlyPosingJoint)
|
||||
continue;
|
||||
|
||||
mPoserAnimator.setJointPosition(avatar, item, vec3, defl);
|
||||
mPoserAnimator.setJointPosition(avatar, item, vec3, frame, defl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2097,7 +2261,8 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
|
|||
|
||||
auto selectedJoints = getUiSelectedPoserJoints();
|
||||
bool savingToExternal = getSavingToBvh();
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
E_BoneDeflectionStyles deflection = getUiSelectedBoneDeflectionStyle();
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
|
||||
for (auto item : selectedJoints)
|
||||
{
|
||||
|
|
@ -2111,14 +2276,17 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
|
|||
bool oppositeJointAlsoSelectedOnUi =
|
||||
std::find(selectedJoints.begin(), selectedJoints.end(), oppositeJoint) != selectedJoints.end();
|
||||
|
||||
bool deflectionDoesOppositeLimbs = !(defl == NONE || defl == DELTAMODE);
|
||||
bool deflectionDoesOppositeLimbs = !(deflection == NONE || deflection == DELTAMODE);
|
||||
if (oppositeJointAlsoSelectedOnUi && deflectionDoesOppositeLimbs && item->dontFlipOnMirror())
|
||||
continue;
|
||||
}
|
||||
|
||||
mPoserAnimator.setJointRotation(avatar, item, absoluteRot, deltaRot, defl,
|
||||
getJointTranslation(item->jointName()), getJointNegation(item->jointName()), savingToExternal,
|
||||
getUiSelectedBoneRotationStyle(item->jointName()));
|
||||
S32 jointNegation = getJointNegation(frame, item->jointName());
|
||||
E_BoneAxisTranslation translation = getJointTranslation(frame, item->jointName());
|
||||
E_RotationStyle style = getUiSelectedBoneRotationStyle(item->jointName());
|
||||
|
||||
mPoserAnimator.setJointRotation(avatar, item, absoluteRot, deltaRot, deflection, frame, translation, jointNegation,
|
||||
savingToExternal, style);
|
||||
}
|
||||
|
||||
if (savingToExternal)
|
||||
|
|
@ -2134,8 +2302,9 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
|
|||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
LLVector3 vec3 = LLVector3(x, y, z);
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
LLVector3 vec3 = LLVector3(x, y, z);
|
||||
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
|
||||
for (auto item : getUiSelectedPoserJoints())
|
||||
{
|
||||
|
|
@ -2143,7 +2312,7 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
|
|||
if (!currentlyPosingJoint)
|
||||
continue;
|
||||
|
||||
mPoserAnimator.setJointScale(avatar, item, vec3, defl);
|
||||
mPoserAnimator.setJointScale(avatar, item, vec3, frame, defl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2161,8 +2330,10 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const
|
|||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return rotation;
|
||||
|
||||
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()),
|
||||
getJointNegation(selectedJoints.front()->jointName()));
|
||||
E_PoserReferenceFrame frame = getReferenceFrame();
|
||||
|
||||
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(frame, selectedJoints.front()->jointName()),
|
||||
getJointNegation(frame, selectedJoints.front()->jointName()));
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
|
@ -2210,18 +2381,25 @@ void FSFloaterPoser::onJointTabSelect()
|
|||
refreshTrackpadCursor();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
refreshScaleSlidersAndSpinners();
|
||||
markSelectedJointsToHighlight();
|
||||
}
|
||||
|
||||
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const
|
||||
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(E_PoserReferenceFrame frame, const std::string& jointName) const
|
||||
{
|
||||
if (jointName.empty())
|
||||
return SWAP_NOTHING;
|
||||
|
||||
bool hasTransformParameter = hasString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName);
|
||||
std::string paramName;
|
||||
if (frame == POSER_FRAME_BONE)
|
||||
paramName = XML_JOINT_TRANSFORM_STRING_PREFIX + jointName;
|
||||
else
|
||||
paramName = XML_JOINT_FRAME_TRANSFORM_PREFIX + jointName;
|
||||
|
||||
bool hasTransformParameter = hasString(paramName);
|
||||
if (!hasTransformParameter)
|
||||
return SWAP_NOTHING;
|
||||
|
||||
std::string paramValue = getString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName);
|
||||
std::string paramValue = getString(paramName);
|
||||
|
||||
if (strstr(paramValue.c_str(), "SWAP_YAW_AND_ROLL"))
|
||||
return SWAP_YAW_AND_ROLL;
|
||||
|
|
@ -2237,18 +2415,24 @@ E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& joi
|
|||
return SWAP_NOTHING;
|
||||
}
|
||||
|
||||
S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
|
||||
S32 FSFloaterPoser::getJointNegation(E_PoserReferenceFrame frame, const std::string& jointName) const
|
||||
{
|
||||
S32 result = NEGATE_NOTHING;
|
||||
|
||||
if (jointName.empty())
|
||||
return result;
|
||||
|
||||
bool hasTransformParameter = hasString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName);
|
||||
if (!hasTransformParameter)
|
||||
std::string paramName;
|
||||
if (frame == POSER_FRAME_BONE)
|
||||
paramName = XML_JOINT_TRANSFORM_STRING_PREFIX + jointName;
|
||||
else
|
||||
paramName = XML_JOINT_FRAME_TRANSFORM_PREFIX + jointName;
|
||||
|
||||
bool hasNegationParameter = hasString(paramName);
|
||||
if (!hasNegationParameter)
|
||||
return result;
|
||||
|
||||
std::string paramValue = getString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName);
|
||||
std::string paramValue = getString(paramName);
|
||||
|
||||
if (strstr(paramValue.c_str(), "NEGATE_YAW"))
|
||||
result |= NEGATE_YAW;
|
||||
|
|
@ -2262,6 +2446,23 @@ S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
|
|||
return result;
|
||||
}
|
||||
|
||||
E_PoserReferenceFrame FSFloaterPoser::getReferenceFrame() const
|
||||
{
|
||||
bool toggleButtonValue = mBtnScreenFrame->getValue().asBoolean();
|
||||
if (toggleButtonValue)
|
||||
return POSER_FRAME_CAMERA;
|
||||
|
||||
toggleButtonValue = mBtnAvatarFrame->getValue().asBoolean();
|
||||
if (toggleButtonValue)
|
||||
return POSER_FRAME_AVATAR;
|
||||
|
||||
toggleButtonValue = mBtnWorldFrame->getValue().asBoolean();
|
||||
if (toggleButtonValue)
|
||||
return POSER_FRAME_WORLD;
|
||||
|
||||
return POSER_FRAME_BONE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event handler for selecting an avatar or animesh on the POSES_AVATAR_SCROLL_LIST_NAME.
|
||||
/// In general this will refresh the views for joints or their proxies, and (dis/en)able elements of the view.
|
||||
|
|
@ -2269,16 +2470,26 @@ S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
|
|||
void FSFloaterPoser::onAvatarSelect()
|
||||
{
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if(avatar)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
bool isSelf = avatar->isSelf();
|
||||
bool haveImplicitPermission = havePermissionToAnimateAvatar(avatar); // self & control avatars you own
|
||||
bool haveExplicitPermission = havePermissionToAnimateOtherAvatar(avatar); // as permissions allow
|
||||
|
||||
if (haveImplicitPermission || haveExplicitPermission)
|
||||
FSToolCompPose::getInstance()->setAvatar(avatar);
|
||||
}
|
||||
mStartStopPosingBtn->setEnabled(couldAnimateAvatar(avatar));
|
||||
else
|
||||
FSToolCompPose::getInstance()->setAvatar(nullptr);
|
||||
|
||||
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);
|
||||
|
||||
mStartStopPosingBtn->setEnabled(haveImplicitPermission);
|
||||
mStartStopPosingBtn->setValue(arePosingSelected);
|
||||
mSetToTposeButton->setEnabled(arePosingSelected);
|
||||
poseControlsEnable(arePosingSelected);
|
||||
|
||||
mSetToTposeButton->setEnabled(haveImplicitPermission && arePosingSelected);
|
||||
poseControlsEnable(arePosingSelected && haveImplicitPermission);
|
||||
|
||||
refreshTextHighlightingOnAvatarScrollList();
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
onJointTabSelect();
|
||||
|
|
@ -2292,7 +2503,16 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const
|
|||
for (LLCharacter* character : LLCharacter::sInstances)
|
||||
{
|
||||
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character);
|
||||
if (!havePermissionToAnimateAvatar(avatar))
|
||||
if (!avatarIsNearbyMe(avatar))
|
||||
continue;
|
||||
|
||||
bool isMuted = LLMuteList::getInstance()->isMuted(avatar->getID());
|
||||
if (isMuted)
|
||||
continue;
|
||||
|
||||
bool isSelfOrCtrl = avatar->isControlAvatar() || avatar->isSelf();
|
||||
|
||||
if (!isSelfOrCtrl)
|
||||
continue;
|
||||
|
||||
avatar_ids.emplace_back(character->getID());
|
||||
|
|
@ -2301,6 +2521,16 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const
|
|||
return avatar_ids;
|
||||
}
|
||||
|
||||
bool FSFloaterPoser::avatarIsNearbyMe(LLCharacter* character) const
|
||||
{
|
||||
if (!gAgentAvatarp || gAgentAvatarp.isNull() || !character)
|
||||
return false;
|
||||
|
||||
LLVector3 separationVector = character->getCharacterPosition() - gAgentAvatarp->getCharacterPosition();
|
||||
|
||||
return separationVector.magVec() < 50.f;
|
||||
}
|
||||
|
||||
uuid_vec_t FSFloaterPoser::getCurrentlyListedAvatarsAndAnimeshes() const
|
||||
{
|
||||
uuid_vec_t avatar_ids;
|
||||
|
|
@ -2351,10 +2581,6 @@ void FSFloaterPoser::onAvatarsRefresh()
|
|||
mAvatarSelectionScrollList->deleteSingleItem(indexToRemove);
|
||||
}
|
||||
|
||||
std::string iconCatagoryName = "Inv_BodyShape";
|
||||
if (hasString("icon_category"))
|
||||
iconCatagoryName = getString("icon_category");
|
||||
|
||||
std::string iconObjectName = "Inv_Object";
|
||||
if (hasString("icon_object"))
|
||||
iconObjectName = getString("icon_object");
|
||||
|
|
@ -2377,10 +2603,17 @@ void FSFloaterPoser::onAvatarsRefresh()
|
|||
if (!LLAvatarNameCache::get(uuid, &av_name))
|
||||
continue;
|
||||
|
||||
bool isMuted = LLMuteList::getInstance()->isMuted(uuid);
|
||||
if (isMuted)
|
||||
continue;
|
||||
|
||||
if (!avatar->isSelf())
|
||||
continue;
|
||||
|
||||
LLSD row;
|
||||
row["columns"][COL_ICON]["column"] = "icon";
|
||||
row["columns"][COL_ICON]["type"] = "icon";
|
||||
row["columns"][COL_ICON]["value"] = iconCatagoryName;
|
||||
row["columns"][COL_ICON]["value"] = getIconNameForAvatar(avatar);
|
||||
row["columns"][COL_NAME]["column"] = "name";
|
||||
row["columns"][COL_NAME]["value"] = av_name.getDisplayName();
|
||||
row["columns"][COL_UUID]["column"] = "uuid";
|
||||
|
|
@ -2423,6 +2656,21 @@ void FSFloaterPoser::onAvatarsRefresh()
|
|||
refreshTextHighlightingOnAvatarScrollList();
|
||||
}
|
||||
|
||||
std::string FSFloaterPoser::getIconNameForAvatar(LLVOAvatar* avatar)
|
||||
{
|
||||
std::string iconName = "Inv_BodyShape";
|
||||
if (hasString("icon_category"))
|
||||
iconName = getString("icon_category");
|
||||
|
||||
if (!avatar)
|
||||
return iconName;
|
||||
|
||||
if (avatar->isControlAvatar() && hasString("icon_object"))
|
||||
return getString("icon_object");
|
||||
|
||||
return iconName;
|
||||
}
|
||||
|
||||
std::string FSFloaterPoser::getControlAvatarName(const LLControlAvatar* avatar)
|
||||
{
|
||||
if (!avatar)
|
||||
|
|
@ -2456,6 +2704,8 @@ void FSFloaterPoser::refreshTextHighlightingOnAvatarScrollList()
|
|||
LLUUID selectedAvatarId = cell->getValue().asUUID();
|
||||
LLVOAvatar* listAvatar = getAvatarByUuid(selectedAvatarId);
|
||||
|
||||
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getIconNameForAvatar(listAvatar));
|
||||
|
||||
if (mPoserAnimator.isPosingAvatar(listAvatar))
|
||||
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
|
||||
else
|
||||
|
|
@ -2530,7 +2780,7 @@ std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPose
|
|||
return tryGetString("icon_rotation_bvh_unlocked");
|
||||
}
|
||||
|
||||
std::string FSFloaterPoser::tryGetString(std::string name)
|
||||
std::string FSFloaterPoser::tryGetString(std::string_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
return "";
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "llfloater.h"
|
||||
#include "lltoolmgr.h"
|
||||
#include "fsposeranimator.h"
|
||||
#include "fsmaniprotatejoint.h"
|
||||
|
||||
class FSVirtualTrackpad;
|
||||
class LLButton;
|
||||
|
|
@ -80,7 +81,8 @@ class FSFloaterPoser : public LLFloater, public LLEditMenuHandler
|
|||
friend class LLFloaterReg;
|
||||
FSFloaterPoser(const LLSD &key);
|
||||
public:
|
||||
void updatePosedBones(const std::string& jointName);
|
||||
void updatePosedBones(const std::string& jointName, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale);
|
||||
LLQuaternion getManipGimbalRotation(const std::string& jointName);
|
||||
void selectJointByName(const std::string& jointName);
|
||||
void undo() override { onUndoLastChange(); };
|
||||
bool canUndo() const override { return true; }
|
||||
|
|
@ -94,6 +96,8 @@ public:
|
|||
void onClose(bool app_quitting) override;
|
||||
void onFocusReceived() override;
|
||||
void onFocusLost() override;
|
||||
virtual void draw() override;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the supplied pose list from the supplued subdirectory.
|
||||
/// </summary>
|
||||
|
|
@ -181,6 +185,13 @@ public:
|
|||
/// <returns>A the collection of UUIDs for nearby avatars.</returns>
|
||||
uuid_vec_t getNearbyAvatarsAndAnimeshes() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied character is within chat range of gAgentAvatar.
|
||||
/// </summary>
|
||||
/// <param name="character">The character to query whether nearby.</param>
|
||||
/// <returns>True if the supplied character is within chat range, otherwise false.</returns>
|
||||
bool avatarIsNearbyMe(LLCharacter* character) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of UUIDs for avatars currently being presented on the UI.
|
||||
/// </summary>
|
||||
|
|
@ -242,12 +253,17 @@ public:
|
|||
void enableVisualManipulators();
|
||||
void disableVisualManipulators();
|
||||
|
||||
// Visual cue for which bone is under the mouse-cursor
|
||||
void drawOnHoverJointHint();
|
||||
void markSelectedJointsToHighlight();
|
||||
|
||||
// UI Event Handlers
|
||||
void onAvatarsRefresh();
|
||||
void onAvatarSelect();
|
||||
void onJointTabSelect();
|
||||
void onToggleMirrorChange();
|
||||
void onToggleSympatheticChange();
|
||||
void onToggleRotationFrameButton(const LLUICtrl* toggleButton);
|
||||
void onToggleVisualManipulators();
|
||||
void setRotationChangeButtons(bool mirror, bool sympathetic);
|
||||
void onUndoLastChange();
|
||||
|
|
@ -280,6 +296,14 @@ public:
|
|||
void refreshTrackpadCursor();
|
||||
void enableOrDisableRedoAndUndoButton();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we have permission to animate the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to animate.</param>
|
||||
/// <returns>True if we have permission to animate, otherwise false.</returns>
|
||||
bool havePermissionToAnimateOtherAvatar(LLVOAvatar* avatar) const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we have permission to animate the supplied avatar.
|
||||
/// </summary>
|
||||
|
|
@ -306,6 +330,7 @@ public:
|
|||
/// This facilitates 'conceptual' conversion of Euler frame to up/down, left/right and roll and is rather subjective.
|
||||
/// Thus, many of these 'conversions' are backed by values in the XML.
|
||||
/// </summary>
|
||||
/// <param name="frame">The reference frame for the change.</param>
|
||||
/// <param name="jointName">The well-known name of the joint, eg: mChest.</param>
|
||||
/// <returns>The axial translation so the oily angles make better sense in terms of up/down/left/right/roll.</returns>
|
||||
/// <remarks>
|
||||
|
|
@ -313,14 +338,21 @@ public:
|
|||
/// No the translation isn't untangling all of that, it's not needed until it is.
|
||||
/// We're not landing on Mars with this code, just offering a user reasonable thumb-twiddlings.
|
||||
/// </remarks>
|
||||
E_BoneAxisTranslation getJointTranslation(const std::string& jointName) const;
|
||||
E_BoneAxisTranslation getJointTranslation(E_PoserReferenceFrame frame, const std::string& jointName) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of E_BoneAxisNegation values for the supplied joint.
|
||||
/// </summary>
|
||||
/// <param name="frame">The reference frame for the change.</param>
|
||||
/// <param name="jointName">The name of the joind to get the axis transformation for.</param>
|
||||
/// <returns>The kind of axis transformation to perform.</returns>
|
||||
S32 getJointNegation(const std::string& jointName) const;
|
||||
S32 getJointNegation(E_PoserReferenceFrame frame, const std::string& jointName) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reference frame for the rotation/position/scale change.
|
||||
/// </summary>
|
||||
/// <returns>The reference frame for the change.</returns>
|
||||
E_PoserReferenceFrame getReferenceFrame() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the axial translation required for joints when saving to BVH.
|
||||
|
|
@ -336,6 +368,13 @@ public:
|
|||
/// </summary>
|
||||
void refreshTextHighlightingOnAvatarScrollList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an appropriate icon for the supplied avatar, based on sharing permission.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to get an icon for.</param>
|
||||
/// <returns>A string with the name of an icon.</returns>
|
||||
std::string getIconNameForAvatar(LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the text on all joints scroll lists based on their state.
|
||||
/// </summary>
|
||||
|
|
@ -367,7 +406,7 @@ public:
|
|||
/// </summary>
|
||||
/// <param name="name">The name of the string.</param>
|
||||
/// <returns>The named string, if it exists, otherwise an empty string.</returns>
|
||||
std::string tryGetString(std::string name);
|
||||
std::string tryGetString(std::string_view name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of an item from the supplied object ID.
|
||||
|
|
@ -450,10 +489,12 @@ public:
|
|||
/// </remarks>
|
||||
static F32 clipRange(F32 value);
|
||||
|
||||
LLToolset* mLastToolset{ nullptr };
|
||||
LLTool* mJointRotTool{ nullptr };
|
||||
|
||||
LLVector3 mLastSliderRotation;
|
||||
LLToolset* mLastToolset{ nullptr };
|
||||
LLTool* mJointRotTool{ nullptr };
|
||||
|
||||
LLVector3 mLastSliderRotation;
|
||||
FSPoserAnimator::FSPoserJoint* mLastSelectedJoint{ nullptr };
|
||||
U64 timeFadeStartedMicrosec;
|
||||
|
||||
FSVirtualTrackpad* mAvatarTrackball{ nullptr };
|
||||
|
||||
|
|
@ -502,6 +543,10 @@ public:
|
|||
LLButton* mUndoChangeBtn{ nullptr };
|
||||
LLButton* mSetToTposeButton{ nullptr };
|
||||
LLButton* mBtnJointRotate{ nullptr };
|
||||
LLButton* mBtnJointReset{ nullptr };
|
||||
LLButton* mBtnWorldFrame{ nullptr };
|
||||
LLButton* mBtnAvatarFrame{ nullptr };
|
||||
LLButton* mBtnScreenFrame{ nullptr };
|
||||
|
||||
LLLineEditor* mPoseSaveNameEditor{ nullptr };
|
||||
|
||||
|
|
@ -516,6 +561,9 @@ public:
|
|||
LLPanel* mMiscJointsPnl{ nullptr };
|
||||
LLPanel* mCollisionVolumesPnl{ nullptr };
|
||||
LLPanel* mPosesLoadSavePnl{ nullptr };
|
||||
LLPanel* mPositionPnl{ nullptr };
|
||||
LLPanel* mMoveTabPnl{ nullptr };
|
||||
LLPanel* mTrackballButtonPnl{ nullptr };
|
||||
|
||||
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr };
|
||||
LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr };
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ FSJointPose::FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume)
|
|||
|
||||
mJointName = joint->getName();
|
||||
mIsCollisionVolume = isCollisionVolume;
|
||||
mJointNumber = joint->getJointNum();
|
||||
|
||||
mCurrentState = FSJointState(joint);
|
||||
}
|
||||
|
|
@ -64,6 +65,8 @@ void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
|
|||
|
||||
if (zeroBase)
|
||||
zeroBaseRotation(true);
|
||||
else
|
||||
mCurrentState.mUserSpecifiedBaseZero = false;
|
||||
|
||||
mCurrentState.mRotation.set(rot);
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
|
|
@ -98,6 +101,8 @@ void FSJointPose::resetJoint()
|
|||
|
||||
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
|
||||
{
|
||||
mModifiedThisSession = true;
|
||||
|
||||
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState;
|
||||
mTimeLastUpdatedCurrentState = std::chrono::system_clock::now();
|
||||
|
||||
|
|
@ -151,27 +156,28 @@ FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToS
|
|||
|
||||
void FSJointPose::recaptureJoint()
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
LLJoint* joint = mJointState->getJoint();
|
||||
if (!joint)
|
||||
return;
|
||||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
|
||||
if (mIsCollisionVolume)
|
||||
{
|
||||
mCurrentState.mPosition.set(LLVector3::zero);
|
||||
mCurrentState.mScale.set(LLVector3::zero);
|
||||
}
|
||||
|
||||
mCurrentState = FSJointState(joint);
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
}
|
||||
|
||||
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
||||
LLQuaternion FSJointPose::updateJointAsDelta(bool zeroBase, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale)
|
||||
{
|
||||
LLJoint* joint = mJointState->getJoint();
|
||||
if (!joint)
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
return mCurrentState.updateFromJoint(joint, zeroBase);
|
||||
|
||||
return mCurrentState.updateFromJointProperties(zeroBase, rotation, position, scale);
|
||||
}
|
||||
|
||||
void FSJointPose::setBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority)
|
||||
|
|
@ -247,6 +253,7 @@ void FSJointPose::reflectRotation()
|
|||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mModifiedThisSession = true;
|
||||
mCurrentState.reflectRotation();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ class FSJointPose
|
|||
/// '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.
|
||||
/// If zeroBase is true, we treat rotations as if in BVH mode: user work.
|
||||
/// If zeroBase is false, we treat as NOT BVH: some existing pose and user work.
|
||||
/// </remarks>
|
||||
void setPublicRotation(bool zeroBase, const LLQuaternion& rot);
|
||||
|
||||
|
|
@ -173,8 +175,11 @@ class FSJointPose
|
|||
/// Recalculates the delta reltive to the base for a new rotation.
|
||||
/// </summary>
|
||||
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
|
||||
/// <param name="rotation">The rotation of the supplied joint.</param>
|
||||
/// <param name="position">The position of the supplied joint.</param>
|
||||
/// <param name="scale">The scale of the supplied joint.</param>
|
||||
/// <returns>The rotation of the public difference between before and after recapture.</returns>
|
||||
LLQuaternion recaptureJointAsDelta(bool zeroBase);
|
||||
LLQuaternion updateJointAsDelta(bool zeroBase, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base rotation to the supplied rotation if the supplied priority is appropriate.
|
||||
|
|
@ -253,6 +258,18 @@ class FSJointPose
|
|||
/// </summary>
|
||||
LLPointer<LLJointState> getJointState() const { return mJointState; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this joint has been modified this session.
|
||||
/// </summary>
|
||||
/// <returns>True if the joint has been changed at all, otherwise false.</returns>
|
||||
bool getJointModified() const { return mModifiedThisSession; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of the joint represented by this.
|
||||
/// </summary>
|
||||
/// <returns>The joint number, derived from LLjoint.</returns>
|
||||
S32 getJointNumber() const { return mJointNumber; }
|
||||
|
||||
class FSJointState
|
||||
{
|
||||
public:
|
||||
|
|
@ -326,15 +343,12 @@ class FSJointPose
|
|||
joint->setScale(mBaseScale);
|
||||
}
|
||||
|
||||
LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase)
|
||||
LLQuaternion updateFromJointProperties(bool zeroBase, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale)
|
||||
{
|
||||
if (!joint)
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
LLQuaternion initalPublicRot = mRotation;
|
||||
LLQuaternion invRot = mBaseRotation;
|
||||
invRot.conjugate();
|
||||
LLQuaternion newPublicRot = joint->getRotation() * invRot;
|
||||
LLQuaternion newPublicRot = rotation * invRot;
|
||||
|
||||
if (zeroBase)
|
||||
{
|
||||
|
|
@ -343,8 +357,8 @@ class FSJointPose
|
|||
}
|
||||
|
||||
mRotation.set(newPublicRot);
|
||||
mPosition.set(joint->getPosition() - mBasePosition);
|
||||
mScale.set(joint->getScale() - mBaseScale);
|
||||
mPosition.set(position - mBasePosition);
|
||||
mScale.set(scale - mBaseScale);
|
||||
|
||||
return newPublicRot *= ~initalPublicRot;
|
||||
}
|
||||
|
|
@ -450,6 +464,13 @@ class FSJointPose
|
|||
/// </summary>
|
||||
bool mIsCollisionVolume{ false };
|
||||
|
||||
S32 mJointNumber = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this joint has ever been changed by poser.
|
||||
/// </summary>
|
||||
bool mModifiedThisSession{ false };
|
||||
|
||||
std::deque<FSJointState> mLastSetJointStates;
|
||||
size_t mUndoneJointStatesIndex = 0;
|
||||
std::chrono::system_clock::time_point mTimeLastUpdatedCurrentState = std::chrono::system_clock::now();
|
||||
|
|
|
|||
|
|
@ -25,11 +25,8 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "fsmaniprotatejoint.h"
|
||||
|
||||
// library includes
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "fsmaniprotatejoint.h"
|
||||
#include "llmath.h"
|
||||
#include "llgl.h"
|
||||
#include "llrender.h"
|
||||
|
|
@ -37,11 +34,10 @@
|
|||
#include "llprimitive.h"
|
||||
#include "llview.h"
|
||||
#include "llfontgl.h"
|
||||
|
||||
#include "llrendersphere.h"
|
||||
#include "llvoavatar.h"
|
||||
#include "lljoint.h"
|
||||
#include "llagent.h" // for gAgent, etc.
|
||||
#include "llagent.h" // for gAgent, etc.
|
||||
#include "llagentcamera.h"
|
||||
#include "llappviewer.h"
|
||||
#include "llcontrol.h"
|
||||
|
|
@ -173,15 +169,18 @@ static void renderStaticSphere(const LLVector3& joint_world_position, const LLCo
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool FSManipRotateJoint::isMouseOverJoint(S32 mouseX, S32 mouseY, const LLVector3& jointWorldPos, F32 jointRadius, F32& outDistanceFromCamera, F32& outRayDistanceFromCenter) const
|
||||
{
|
||||
// LL_INFOS("FSManipRotateJoint") << "Checking mouse("<< mouseX << "," << mouseY << ") over joint at: " << jointWorldPos << LL_ENDL;
|
||||
|
||||
auto joint_center = gAgent.getPosGlobalFromAgent( jointWorldPos );
|
||||
if (!mJoint || !mAvatar)
|
||||
return false;
|
||||
|
||||
if (mAvatar->isDead() || !mAvatar->isFullyLoaded())
|
||||
return false;
|
||||
|
||||
auto joint_center = mAvatar->getPosGlobalFromAgent(jointWorldPos);
|
||||
|
||||
// centre in *agent* space
|
||||
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(joint_center);
|
||||
LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(joint_center);
|
||||
LLVector3 ray_pt, ray_dir;
|
||||
LLManipRotate::mouseToRay(mouseX, mouseY, &ray_pt, &ray_dir);
|
||||
|
||||
|
|
@ -201,9 +200,9 @@ bool FSManipRotateJoint::isMouseOverJoint(S32 mouseX, S32 mouseY, const LLVector
|
|||
outDistanceFromCamera = proj_len - offset; // distance along the ray to the front intersection
|
||||
outRayDistanceFromCenter = offset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
@ -232,7 +231,6 @@ const std::vector<std::string_view> FSManipRotateJoint::sSelectableJoints =
|
|||
{ "mHipRight" },
|
||||
{ "mKneeRight" },
|
||||
{ "mAnkleRight" },
|
||||
|
||||
};
|
||||
|
||||
const std::unordered_map<FSManipRotateJoint::e_manip_part, FSManipRotateJoint::RingRenderParams> FSManipRotateJoint::sRingParams =
|
||||
|
|
@ -241,6 +239,7 @@ const std::unordered_map<FSManipRotateJoint::e_manip_part, FSManipRotateJoint::R
|
|||
{ LL_ROT_Y, { LL_ROT_Y, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), 90.f, LLVector3(1.f,0.f,0.f), LLColor4(0.f,1.f,0.f,1.f), LLColor4(0.f,1.f,0.f,0.3f), 1 } },
|
||||
{ LL_ROT_X, { LL_ROT_X, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), 90.f, LLVector3(0.f,1.f,0.f), LLColor4(1.f,0.f,0.f,1.f), LLColor4(1.f,0.f,0.f,0.3f), 0 } }
|
||||
};
|
||||
|
||||
// Helper function: Builds an alignment quaternion from the computed bone axes.
|
||||
// This quaternion rotates from the default coordinate system (assumed to be
|
||||
// X = (1,0,0), Y = (0,1,0), Z = (0,0,1)) into the bone’s natural coordinate system.
|
||||
|
|
@ -268,7 +267,7 @@ FSManipRotateJoint::BoneAxes FSManipRotateJoint::computeBoneAxes() const
|
|||
BoneAxes axes;
|
||||
|
||||
// Use 0,0,0 as local start for joint.
|
||||
LLVector3 joint_local_pos (0.f,0.f,0.f);
|
||||
LLVector3 joint_local_pos(0.f,0.f,0.f);
|
||||
|
||||
// Transform the local endpoint (mEnd) into world space.
|
||||
LLVector3 localEnd = mJoint->getEnd();
|
||||
|
|
@ -331,13 +330,14 @@ void FSManipRotateJoint::highlightHoverSpheres(S32 mouseX, S32 mouseY)
|
|||
mHighlightedJoint = nullptr; // reset the highlighted joint
|
||||
|
||||
// Iterate through the avatar's joint map.
|
||||
F32 nearest_hit_distance = 0.f;
|
||||
F32 nearest_ray_distance = 0.f;
|
||||
LLJoint * nearest_joint = nullptr;
|
||||
for ( const auto& entry : getSelectableJoints())
|
||||
F32 nearest_hit_distance = 0.f;
|
||||
F32 nearest_ray_distance = 0.f;
|
||||
LLJoint* nearest_joint = nullptr;
|
||||
LLCachedControl<F32> target_radius(gSavedSettings, "FSManipRotateJointTargetSize", 0.03f);
|
||||
|
||||
for (const auto& entry : getSelectableJoints())
|
||||
{
|
||||
|
||||
LLJoint* joint = mAvatar->getJoint(std::string(entry));
|
||||
LLJoint* joint = mAvatar->getJoint(std::string(entry));
|
||||
if (!joint)
|
||||
continue;
|
||||
|
||||
|
|
@ -347,23 +347,22 @@ void FSManipRotateJoint::highlightHoverSpheres(S32 mouseX, S32 mouseY)
|
|||
|
||||
// Retrieve the joint's world position (in agent space).
|
||||
LLVector3 jointWorldPos = joint->getWorldPosition();
|
||||
LLCachedControl<F32> target_radius(gSavedSettings, "FSManipRotateJointTargetSize", 0.03f);
|
||||
F32 distance_from_camera;
|
||||
F32 distance_from_joint;
|
||||
if (isMouseOverJoint(mouseX, mouseY, jointWorldPos, target_radius, distance_from_camera, distance_from_joint) == true)
|
||||
if (!isMouseOverJoint(mouseX, mouseY, jointWorldPos, target_radius, distance_from_camera, distance_from_joint))
|
||||
continue;
|
||||
|
||||
// we want to highlight the closest
|
||||
// If there is no joint or this joint is a closer hit than the previous one
|
||||
if (!nearest_joint || nearest_ray_distance > distance_from_camera ||
|
||||
(nearest_ray_distance == distance_from_camera && nearest_hit_distance > distance_from_joint))
|
||||
{
|
||||
// we want to highlight the closest
|
||||
// If there is no joint or
|
||||
// this joint is a closer hit than the previous one
|
||||
if (!nearest_joint || nearest_ray_distance > distance_from_camera ||
|
||||
(nearest_ray_distance == distance_from_camera && nearest_hit_distance > distance_from_joint))
|
||||
{
|
||||
nearest_joint = joint;
|
||||
nearest_hit_distance = distance_from_joint;
|
||||
nearest_ray_distance = distance_from_camera;
|
||||
}
|
||||
nearest_joint = joint;
|
||||
nearest_hit_distance = distance_from_joint;
|
||||
nearest_ray_distance = distance_from_camera;
|
||||
}
|
||||
}
|
||||
|
||||
mHighlightedJoint = nearest_joint;
|
||||
}
|
||||
|
||||
|
|
@ -376,19 +375,26 @@ FSManipRotateJoint::FSManipRotateJoint(LLToolComposite* composite)
|
|||
void FSManipRotateJoint::setJoint(LLJoint* joint)
|
||||
{
|
||||
mJoint = joint;
|
||||
if (!mJoint)
|
||||
return;
|
||||
|
||||
// Save initial rotation as baseline for delta rotation
|
||||
if (mJoint)
|
||||
{
|
||||
mSavedJointRot = mJoint->getWorldRotation();
|
||||
mBoneAxes = computeBoneAxes();
|
||||
mNaturalAlignmentQuat = computeAlignmentQuat(mBoneAxes);
|
||||
}
|
||||
mSavedJointRot = getSelectedJointWorldRotation();
|
||||
mBoneAxes = computeBoneAxes();
|
||||
mNaturalAlignmentQuat = computeAlignmentQuat(mBoneAxes);
|
||||
}
|
||||
|
||||
void FSManipRotateJoint::setAvatar(LLVOAvatar* avatar)
|
||||
{
|
||||
mAvatar = avatar;
|
||||
|
||||
if (!avatar)
|
||||
mJoint = nullptr;
|
||||
|
||||
if (!mJoint)
|
||||
return;
|
||||
|
||||
setJoint(avatar->getJoint(mJoint->getJointNum()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -403,7 +409,7 @@ void FSManipRotateJoint::handleSelect()
|
|||
// Not entirely sure this is needed in the current implementation.
|
||||
if (mJoint)
|
||||
{
|
||||
mSavedJointRot = mJoint->getWorldRotation();
|
||||
mSavedJointRot = getSelectedJointWorldRotation();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -422,15 +428,12 @@ void FSManipRotateJoint::handleSelect()
|
|||
*/
|
||||
bool FSManipRotateJoint::updateVisiblity()
|
||||
{
|
||||
if (!mJoint)
|
||||
{
|
||||
// No joint to manipulate, not visible
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasMouseCapture())
|
||||
{
|
||||
mRotationCenter = gAgent.getPosGlobalFromAgent( mJoint->getWorldPosition() );
|
||||
mRotationCenter = mAvatar->getPosGlobalFromAgent(mJoint->getWorldPosition());
|
||||
mCamEdgeOn = false;
|
||||
}
|
||||
|
||||
|
|
@ -439,7 +442,7 @@ bool FSManipRotateJoint::updateVisiblity()
|
|||
//Assume that UI scale factor is equivalent for X and Y axis
|
||||
F32 ui_scale_factor = LLUI::getScaleFactor().mV[VX];
|
||||
|
||||
const LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal( mRotationCenter ); // Convert from world/agent to global
|
||||
const LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter); // Convert from world/agent to global
|
||||
|
||||
const auto * viewer_camera = LLViewerCamera::getInstance();
|
||||
visible = viewer_camera->projectPosAgentToScreen(agent_space_center, mCenterScreen );
|
||||
|
|
@ -473,7 +476,6 @@ bool FSManipRotateJoint::updateVisiblity()
|
|||
return visible;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Updates the scale of a specific manipulator part.
|
||||
*
|
||||
|
|
@ -500,21 +502,29 @@ void FSManipRotateJoint::renderRingPass(const RingRenderParams& params, float ra
|
|||
{
|
||||
gGL.pushMatrix();
|
||||
{
|
||||
// If an extra rotation is specified, apply it.
|
||||
if (params.extraRotateAngle != 0.f)
|
||||
{
|
||||
gGL.rotatef(params.extraRotateAngle, params.extraRotateAxis.mV[0],
|
||||
params.extraRotateAxis.mV[1], params.extraRotateAxis.mV[2]);
|
||||
}
|
||||
// If an extra rotation is specified, apply it.
|
||||
if (params.extraRotateAngle != 0.f)
|
||||
{
|
||||
gGL.rotatef(params.extraRotateAngle, params.extraRotateAxis.mV[0], params.extraRotateAxis.mV[1], params.extraRotateAxis.mV[2]);
|
||||
}
|
||||
// Get the appropriate scale value from mManipulatorScales.
|
||||
float scaleVal = 1.f;
|
||||
switch (params.scaleIndex)
|
||||
{
|
||||
case 0: scaleVal = mManipulatorScales.mV[VX]; break;
|
||||
case 1: scaleVal = mManipulatorScales.mV[VY]; break;
|
||||
case 2: scaleVal = mManipulatorScales.mV[VZ]; break;
|
||||
case 3: scaleVal = mManipulatorScales.mV[VW]; break;
|
||||
default: break;
|
||||
case 0:
|
||||
scaleVal = mManipulatorScales.mV[VX];
|
||||
break;
|
||||
case 1:
|
||||
scaleVal = mManipulatorScales.mV[VY];
|
||||
break;
|
||||
case 2:
|
||||
scaleVal = mManipulatorScales.mV[VZ];
|
||||
break;
|
||||
case 3:
|
||||
scaleVal = mManipulatorScales.mV[VW];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gGL.scalef(scaleVal, scaleVal, scaleVal);
|
||||
gl_ring(radius, width, params.primaryColor, params.secondaryColor, CIRCLE_STEPS, pass);
|
||||
|
|
@ -552,7 +562,7 @@ void FSManipRotateJoint::renderManipulatorRings(const LLVector3& agent_space_cen
|
|||
|
||||
for (int pass = 0; pass < 2; ++pass)
|
||||
{
|
||||
if( mManipPart == LL_NO_PART || mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
|
||||
if (mManipPart == LL_NO_PART || mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
|
||||
{
|
||||
renderCenterSphere( mRadiusMeters);
|
||||
for (auto& ring_params : sRingParams)
|
||||
|
|
@ -565,16 +575,17 @@ void FSManipRotateJoint::renderManipulatorRings(const LLVector3& agent_space_cen
|
|||
}
|
||||
renderRingPass(params, mRadiusMeters, width_meters, pass);
|
||||
}
|
||||
|
||||
if( mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
|
||||
{
|
||||
static const auto roll_color = LLColor4(1.f,0.65f,0.0f,0.4f);
|
||||
static const auto roll_color = LLColor4(1.f, 0.65f, 0.0f, 0.4f);
|
||||
updateManipulatorScale(mManipPart, mManipulatorScales);
|
||||
gGL.pushMatrix();
|
||||
{
|
||||
// Cancel the rotation applied earlier:
|
||||
LLMatrix4 inv_rot_mat(rotation);
|
||||
inv_rot_mat.invert();
|
||||
gGL.multMatrix((GLfloat*)inv_rot_mat.mMatrix);
|
||||
gGL.multMatrix((GLfloat*)inv_rot_mat.mMatrix);
|
||||
renderCenterCircle( mRadiusMeters*1.2f, roll_color, roll_color );
|
||||
}
|
||||
gGL.popMatrix();
|
||||
|
|
@ -619,7 +630,7 @@ void FSManipRotateJoint::renderCenterCircle(const F32 radius, const LLColor4& no
|
|||
|
||||
LLVector3 rotationAxis = defaultNormal % targetNormal; // Cross product.
|
||||
F32 dot = defaultNormal * targetNormal; // Dot product.
|
||||
F32 angle = acosf(dot) * RAD_TO_DEG; // Convert to degrees.
|
||||
F32 angle = acosf(dot) * RAD_TO_DEG; // Convert to degrees.
|
||||
|
||||
if (rotationAxis.magVec() > 0.001f)
|
||||
{
|
||||
|
|
@ -664,16 +675,15 @@ void FSManipRotateJoint::renderCenterSphere(const F32 radius, const LLColor4& no
|
|||
// Use an average of the manipulator scales when no specific part is selected
|
||||
scale *= (mManipulatorScales.mV[VX] + mManipulatorScales.mV[VY] + mManipulatorScales.mV[VZ] + mManipulatorScales.mV[VW]) / 4.0f;
|
||||
}
|
||||
|
||||
|
||||
gGL.scalef(scale, scale, scale);
|
||||
gSphere.render();
|
||||
|
||||
gGL.flush();
|
||||
gGL.flush();
|
||||
}
|
||||
gGL.popMatrix();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Renders the joint rotation manipulator and associated visual elements.
|
||||
*
|
||||
|
|
@ -697,72 +707,65 @@ void FSManipRotateJoint::renderCenterSphere(const F32 radius, const LLColor4& no
|
|||
*/
|
||||
void FSManipRotateJoint::render()
|
||||
{
|
||||
// Early-out if no joint or avatar.
|
||||
// Needs more something: if they log out while dots on them, asplode
|
||||
if (!mJoint || !mAvatar || mAvatar->isDead())
|
||||
{
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// update visibility and rotation center.
|
||||
bool activeJointVisible = updateVisiblity();
|
||||
|
||||
// Setup GL state.
|
||||
LLGLSUIDefault gls_ui;
|
||||
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
|
||||
LLGLDepthTest gls_depth(GL_TRUE);
|
||||
LLGLEnable gl_blend(GL_BLEND);
|
||||
|
||||
|
||||
// Iterate through the avatar's joint map.
|
||||
// If a joint other than the currently selected is highlighted, render a pulsing sphere.
|
||||
// otherwise a small static sphere
|
||||
LLCachedControl<bool> show_joint_markers(gSavedSettings, "FSManipShowJointMarkers", true);
|
||||
LLVector3 jointLocation;
|
||||
for (const auto& entry : getSelectableJoints())
|
||||
{
|
||||
LLJoint* joint = mAvatar->getJoint(std::string(entry));
|
||||
if (!joint)
|
||||
continue;
|
||||
|
||||
// Update the joint's world matrix to ensure its position is current.
|
||||
joint->updateWorldMatrixParent();
|
||||
joint->updateWorldMatrix();
|
||||
|
||||
if( joint == mHighlightedJoint && joint != mJoint )
|
||||
if (joint == mJoint)
|
||||
continue;
|
||||
|
||||
jointLocation = joint->getWorldPosition();
|
||||
if (joint == mHighlightedJoint)
|
||||
{
|
||||
renderPulsingSphere(joint->getWorldPosition());
|
||||
}
|
||||
else if( joint != mJoint )
|
||||
{
|
||||
// Render a static sphere for the joint being manipulated.
|
||||
LLCachedControl<bool> show_joint_markers(gSavedSettings, "FSManipShowJointMarkers", true);
|
||||
if(show_joint_markers)
|
||||
{
|
||||
renderStaticSphere(joint->getWorldPosition(), LLColor4(1.f, 0.5f, 0.f, 0.5f), 0.01f);
|
||||
}
|
||||
renderPulsingSphere(jointLocation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (show_joint_markers)
|
||||
renderStaticSphere(jointLocation, LLColor4(1.f, 0.5f, 0.f, 0.5f), 0.01f);
|
||||
}
|
||||
|
||||
if (!activeJointVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update joint world matrices.
|
||||
mJoint->updateWorldMatrixParent();
|
||||
mJoint->updateWorldMatrix();
|
||||
|
||||
const LLQuaternion joint_world_rotation = mJoint->getWorldRotation();
|
||||
|
||||
const LLQuaternion joint_world_rotation = getSelectedJointWorldRotation();
|
||||
const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT;
|
||||
|
||||
LLQuaternion currentLocalRot = mJoint->getRotation();
|
||||
|
||||
LLQuaternion rotatedNaturalAlignment = mNaturalAlignmentQuat * currentLocalRot;
|
||||
|
||||
// Compute the final world alignment:
|
||||
LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot;
|
||||
|
||||
const LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
const LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
|
||||
LLQuaternion active_rotation = use_natural_direction? final_world_alignment : joint_world_rotation;
|
||||
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
|
||||
LLQuaternion active_rotation = use_natural_direction ? final_world_alignment : joint_world_rotation;
|
||||
active_rotation.normalize();
|
||||
|
||||
// Render the manipulator rings in a separate function.
|
||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||
renderAxes(agent_space_center, mRadiusMeters * 1.5f, active_rotation);
|
||||
|
|
@ -779,13 +782,13 @@ void FSManipRotateJoint::renderAxes(const LLVector3& agent_space_center, F32 siz
|
|||
LLGLDepthTest gls_depth(GL_FALSE);
|
||||
gGL.pushMatrix();
|
||||
gGL.translatef(agent_space_center.mV[VX], agent_space_center.mV[VY], agent_space_center.mV[VZ]);
|
||||
|
||||
|
||||
LLMatrix4 rot_mat(rotation);
|
||||
|
||||
gGL.multMatrix((GLfloat*)rot_mat.mMatrix);
|
||||
|
||||
gGL.begin(LLRender::LINES);
|
||||
|
||||
|
||||
// X-axis (Red)
|
||||
gGL.color4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
gGL.vertex3f(-size, 0.0f, 0.0f);
|
||||
|
|
@ -875,9 +878,7 @@ void FSManipRotateJoint::renderNameXYZ(const LLQuaternion& rot)
|
|||
S32 vertical_offset = window_center_y - VERTICAL_OFFSET;
|
||||
|
||||
LLVector3 euler_angles;
|
||||
rot.getEulerAngles(&euler_angles.mV[0],
|
||||
&euler_angles.mV[1],
|
||||
&euler_angles.mV[2]);
|
||||
rot.getEulerAngles(&euler_angles.mV[0], &euler_angles.mV[1], &euler_angles.mV[2]);
|
||||
euler_angles *= RAD_TO_DEG;
|
||||
for (S32 i = 0; i < 3; ++i)
|
||||
{
|
||||
|
|
@ -925,15 +926,6 @@ void FSManipRotateJoint::renderNameXYZ(const LLQuaternion& rot)
|
|||
base_y += 20.f;
|
||||
renderTextWithShadow(llformat("Joint: %s", mJoint->getName().c_str()), window_center_x - 130.f, base_y, LLColor4(1.f, 0.1f, 1.f, 1.f));
|
||||
renderTextWithShadow(llformat("Manip: %s%c", getManipPartString(mManipPart).c_str(), mCamEdgeOn?'*':' '), window_center_x + 30.f, base_y, LLColor4(1.f, 1.f, .1f, 1.f));
|
||||
if (mManipPart != LL_NO_PART)
|
||||
{
|
||||
LL_INFOS("FSManipRotateJoint") << "Joint: " << mJoint->getName()
|
||||
<< ", Manip: " << getManipPartString(mManipPart)
|
||||
<< ", Quaternion: " << rot
|
||||
<< ", Euler Angles: " << mLastEuler
|
||||
<< ", Delta Angle: " << mLastAngle * RAD_TO_DEG
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
gGL.popMatrix();
|
||||
|
||||
|
|
@ -954,48 +946,46 @@ void FSManipRotateJoint::renderActiveRing( F32 radius, F32 width, const LLColor4
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------
|
||||
// Overriding because the base uses mObjectSelection->getFirstMoveableObject(true)
|
||||
// Not sure we use it though...TBC (see mouse down on part instead)
|
||||
bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask)
|
||||
{
|
||||
if (!mJoint)
|
||||
{
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Highlight the manipulator as before.
|
||||
highlightManipulators(x, y);
|
||||
|
||||
if (mHighlightedPart != LL_NO_PART)
|
||||
if (mHighlightedPart == LL_NO_PART)
|
||||
return false;
|
||||
|
||||
mManipPart = (EManipPart)mHighlightedPart;
|
||||
|
||||
// Get the joint's center in agent space.
|
||||
LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
// Use the existing function to get the intersection point.
|
||||
LLVector3 intersection = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters);
|
||||
|
||||
// Check if the returned intersection is valid.
|
||||
if (intersection.isExactlyZero())
|
||||
{
|
||||
mManipPart = (EManipPart)mHighlightedPart;
|
||||
|
||||
// Get the joint's center in agent space.
|
||||
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
// Use the existing function to get the intersection point.
|
||||
LLVector3 intersection = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters);
|
||||
|
||||
// Check if the returned intersection is valid.
|
||||
if (intersection.isExactlyZero())
|
||||
{
|
||||
// Treat this as a "raycast miss" and do not capture the mouse.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save the valid intersection point.
|
||||
mInitialIntersection = intersection;
|
||||
// Also store the joint's current rotation.
|
||||
mSavedJointRot = mJoint->getWorldRotation();
|
||||
|
||||
// Capture the mouse for dragging.
|
||||
setMouseCapture(true);
|
||||
return true;
|
||||
}
|
||||
// Treat this as a "raycast miss" and do not capture the mouse.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save the valid intersection point.
|
||||
mInitialIntersection = intersection;
|
||||
// Also store the joint's current rotation.
|
||||
mSavedJointRot = getSelectedJointWorldRotation();
|
||||
|
||||
// Capture the mouse for dragging.
|
||||
setMouseCapture(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1017,24 +1007,27 @@ bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask)
|
|||
*/
|
||||
bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
|
||||
{
|
||||
auto * poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
// For joint manipulation, require both a valid joint and avatar.
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return false;
|
||||
|
||||
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
if (!poser)
|
||||
return false;
|
||||
|
||||
// Determine which ring (axis) is under the mouse, also highlights selectable joints.
|
||||
highlightManipulators(x, y);
|
||||
// For joint manipulation, require both a valid joint and avatar.
|
||||
if (!mJoint || !mAvatar || mAvatar->isDead() || !poser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
poser->setFocus(true);
|
||||
S32 hit_part = mHighlightedPart;
|
||||
|
||||
// Save the joint’s current world rotation as the basis for the drag.
|
||||
mSavedJointRot = mJoint->getWorldRotation();
|
||||
|
||||
mSavedJointRot = getSelectedJointWorldRotation();
|
||||
mLastSetRotation.set(LLQuaternion());
|
||||
mManipPart = (EManipPart)hit_part;
|
||||
|
||||
// Convert rotation center from global to agent space.
|
||||
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
// based on mManipPArt (set in highlightmanipulators). decide whether we are constrained or not in the rotation
|
||||
if (mManipPart == LL_ROT_GENERAL)
|
||||
|
|
@ -1098,35 +1091,27 @@ bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
|
|||
// We use mouseUp to update the UI, updating it during the drag is too slow.
|
||||
bool FSManipRotateJoint::handleMouseUp(S32 x, S32 y, MASK mask)
|
||||
{
|
||||
|
||||
auto * poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
if (hasMouseCapture())
|
||||
{
|
||||
// Update the UI, by causing it to read back the position of the selected joints and aply those relative to the base rot
|
||||
if (poser && mJoint)
|
||||
{
|
||||
poser->updatePosedBones(mJoint->getName());
|
||||
}
|
||||
|
||||
// Release mouse
|
||||
{
|
||||
setMouseCapture(false);
|
||||
mManipPart = LL_NO_PART;
|
||||
mLastAngle = 0.0f;
|
||||
mLastAngle = 0.0f;
|
||||
mCamEdgeOn = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(mHighlightedJoint)
|
||||
else if (mHighlightedJoint)
|
||||
{
|
||||
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
if (poser)
|
||||
{
|
||||
poser->selectJointByName(mHighlightedJoint->getName());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does all the hard work of working out what inworld control we are interacting with
|
||||
*
|
||||
|
|
@ -1143,7 +1128,7 @@ void FSManipRotateJoint::highlightManipulators(S32 x, S32 y)
|
|||
mHighlightedPart = LL_NO_PART;
|
||||
// Instead of using mObjectSelection->getFirstMoveableObject(),
|
||||
// simply require that the joint (and the avatar) is valid.
|
||||
if (!mJoint || !mAvatar || mAvatar->isDead())
|
||||
if (!isAvatarJointSafeToUse())
|
||||
{
|
||||
highlightHoverSpheres(x, y);
|
||||
gViewerWindow->setCursor(UI_CURSOR_ARROW);
|
||||
|
|
@ -1153,25 +1138,24 @@ void FSManipRotateJoint::highlightManipulators(S32 x, S32 y)
|
|||
// Decide which rotation to use based on a user toggle.
|
||||
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
|
||||
// Compute the rotation center in agent space.
|
||||
LLVector3 agent_space_rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
LLVector3 agent_space_rotation_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
// Update joint world matrices.
|
||||
mJoint->updateWorldMatrixParent();
|
||||
mJoint->updateWorldMatrix();
|
||||
|
||||
const LLQuaternion joint_world_rotation = mJoint->getWorldRotation();
|
||||
const LLQuaternion joint_world_rotation = getSelectedJointWorldRotation();
|
||||
|
||||
const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT;
|
||||
|
||||
LLQuaternion currentLocalRot = mJoint->getRotation();
|
||||
|
||||
|
||||
LLQuaternion rotatedNaturalAlignment = mNaturalAlignmentQuat * currentLocalRot;
|
||||
rotatedNaturalAlignment.normalize();
|
||||
// Compute the final world alignment:
|
||||
LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot;
|
||||
final_world_alignment.normalize();
|
||||
|
||||
|
||||
LLQuaternion joint_rot = use_natural_direction ? final_world_alignment : joint_world_rotation;
|
||||
|
||||
// Compute the three local axes in world space.
|
||||
|
|
@ -1308,14 +1292,18 @@ bool FSManipRotateJoint::handleHover(S32 x, S32 y, MASK mask)
|
|||
{
|
||||
highlightManipulators(x, y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LLQuaternion FSManipRotateJoint::dragUnconstrained(S32 x, S32 y)
|
||||
{
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return LLQuaternion();
|
||||
|
||||
// Get the camera position and the joint’s pivot (in agent space)
|
||||
LLVector3 cam = gAgentCamera.getCameraPositionAgent();
|
||||
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
|
||||
// Compute the current intersection on the sphere.
|
||||
mMouseCur = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters);
|
||||
|
|
@ -1388,27 +1376,29 @@ static LLQuaternion extractTwist(const LLQuaternion& rot, const LLVector3& axis)
|
|||
LLVector3 proj = axis * dot; // proj is now purely along 'axis'
|
||||
|
||||
// Build the “twist” quaternion from (proj, w), then renormalize
|
||||
LLQuaternion twist(proj.mV[VX],
|
||||
proj.mV[VY],
|
||||
proj.mV[VZ],
|
||||
w);
|
||||
LLQuaternion twist(proj.mV[VX], proj.mV[VY], proj.mV[VZ], w);
|
||||
if (w < 0.f)
|
||||
{
|
||||
{
|
||||
twist = -twist;
|
||||
}
|
||||
twist.normalize();
|
||||
return twist;
|
||||
}
|
||||
|
||||
LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
|
||||
{
|
||||
if (!isAvatarJointSafeToUse())
|
||||
return LLQuaternion();
|
||||
|
||||
// Get the constraint axis from our joint manipulator.
|
||||
LLVector3 constraint_axis = getConstraintAxis();
|
||||
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
|
||||
LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
|
||||
if (mCamEdgeOn)
|
||||
{
|
||||
LLQuaternion freeRot = dragUnconstrained(x, y);
|
||||
return extractTwist(freeRot, constraint_axis);
|
||||
}
|
||||
|
||||
// Project the current mouse position onto the plane defined by the constraint axis.
|
||||
LLVector3 projected_mouse;
|
||||
bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, agent_space_center, constraint_axis);
|
||||
|
|
@ -1416,6 +1406,7 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
|
|||
{
|
||||
return LLQuaternion::DEFAULT;
|
||||
}
|
||||
|
||||
projected_mouse -= agent_space_center;
|
||||
projected_mouse.normalize();
|
||||
|
||||
|
|
@ -1424,6 +1415,7 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
|
|||
initial_proj -= (initial_proj * constraint_axis) * constraint_axis;
|
||||
initial_proj.normalize();
|
||||
|
||||
//float angle = acos(initial_proj * projected_mouse); // angle in (-pi, pi)
|
||||
// Compute the signed angle using atan2.
|
||||
// The numerator is the magnitude of the cross product projected along the constraint axis.
|
||||
float numerator = (initial_proj % projected_mouse) * constraint_axis;
|
||||
|
|
@ -1431,14 +1423,16 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
|
|||
float denominator = initial_proj * projected_mouse;
|
||||
float angle = atan2(numerator, denominator); // angle in (-pi, pi)
|
||||
mLastAngle = angle;
|
||||
|
||||
return LLQuaternion(angle, constraint_axis);
|
||||
}
|
||||
|
||||
void FSManipRotateJoint::drag(S32 x, S32 y)
|
||||
{
|
||||
if (!updateVisiblity() || !mJoint) return;
|
||||
if (!updateVisiblity())
|
||||
return;
|
||||
|
||||
LLQuaternion delta_rot;
|
||||
LLQuaternion delta_send, delta_rot;
|
||||
if (mManipPart == LL_ROT_GENERAL)
|
||||
{
|
||||
delta_rot = dragUnconstrained(x, y);
|
||||
|
|
@ -1447,10 +1441,31 @@ void FSManipRotateJoint::drag(S32 x, S32 y)
|
|||
{
|
||||
delta_rot = dragConstrained(x, y);
|
||||
}
|
||||
|
||||
// Compose the saved joint rotation with the delta to compute the new world rotation.
|
||||
LLQuaternion new_world_rot = mSavedJointRot * delta_rot;
|
||||
mJoint->setWorldRotation(new_world_rot);
|
||||
|
||||
delta_send.set(delta_rot);
|
||||
delta_send *= ~mLastSetRotation;
|
||||
mLastSetRotation.set(delta_rot);
|
||||
delta_send = mSavedJointRot * delta_send * ~mSavedJointRot;
|
||||
|
||||
switch (mReferenceFrame)
|
||||
{
|
||||
case POSER_FRAME_CAMERA:
|
||||
case POSER_FRAME_AVATAR:
|
||||
delta_send.conjugate();
|
||||
break;
|
||||
|
||||
case POSER_FRAME_WORLD:
|
||||
delta_send.mQ[VX] *= -1;
|
||||
break;
|
||||
|
||||
case POSER_FRAME_BONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
if (poser && mJoint)
|
||||
poser->updatePosedBones(mJoint->getName(), delta_send, LLVector3::zero, LLVector3::zero);
|
||||
}
|
||||
|
||||
// set mConstrainedAxis based on mManipParat and returns it too.
|
||||
|
|
@ -1478,10 +1493,10 @@ LLVector3 FSManipRotateJoint::setConstraintAxis()
|
|||
// Transform the local axis into world space using the joint's world rotation.
|
||||
if (mJoint)
|
||||
{
|
||||
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
|
||||
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
|
||||
LLQuaternion active_rotation;
|
||||
if (use_natural_direction)
|
||||
{
|
||||
{
|
||||
// Get the joint's current local rotation.
|
||||
LLQuaternion currentLocalRot = mJoint->getRotation();
|
||||
const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT;
|
||||
|
|
@ -1493,12 +1508,41 @@ LLVector3 FSManipRotateJoint::setConstraintAxis()
|
|||
}
|
||||
else
|
||||
{
|
||||
active_rotation = mJoint->getWorldRotation();
|
||||
active_rotation = getSelectedJointWorldRotation();
|
||||
}
|
||||
axis = axis * active_rotation;
|
||||
axis.normalize();
|
||||
}
|
||||
}
|
||||
|
||||
mConstraintAxis = axis;
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
LLQuaternion FSManipRotateJoint::getSelectedJointWorldRotation()
|
||||
{
|
||||
LLQuaternion joinRot;
|
||||
if (!mJoint || !mAvatar)
|
||||
return joinRot;
|
||||
|
||||
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
|
||||
if (!poser)
|
||||
return joinRot;
|
||||
|
||||
return poser->getManipGimbalRotation(mJoint->getName());
|
||||
}
|
||||
|
||||
bool FSManipRotateJoint::isAvatarJointSafeToUse()
|
||||
{
|
||||
if (!mJoint || !mAvatar)
|
||||
return false;
|
||||
|
||||
if (mAvatar->isDead() || !mAvatar->isFullyLoaded())
|
||||
{
|
||||
setAvatar(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @file fsmaniproatejoint.h
|
||||
* @file fsmaniprotatejoint.h
|
||||
* @brief custom manipulator for rotating joints
|
||||
*
|
||||
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
|
||||
|
|
@ -32,11 +32,22 @@
|
|||
#include "llmaniprotate.h"
|
||||
|
||||
class LLJoint;
|
||||
class LLVOAvatar; // or LLVOAvatarSelf, etc.
|
||||
class LLVOAvatar; // for LLVOAvatarSelf, etc.
|
||||
|
||||
/// <summary>
|
||||
/// A set of reference frames for presenting the gimbal within.
|
||||
/// </summary>
|
||||
typedef enum E_PoserReferenceFrame
|
||||
{
|
||||
POSER_FRAME_BONE = 0, // frame is the bone the gimbal is centered on
|
||||
POSER_FRAME_WORLD = 1, // frame is world North-South-East-West-Up-Down
|
||||
POSER_FRAME_AVATAR = 2, // frame is mPelvis rotation
|
||||
POSER_FRAME_CAMERA = 3, // frame is defined by vector of camera position to bone position
|
||||
} E_PoserReferenceFrame;
|
||||
|
||||
namespace {
|
||||
const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 85.f * DEG_TO_RAD ); // cos() is not constexpr til c++26
|
||||
constexpr F32 RADIUS_PIXELS = 100.f; // size in screen space
|
||||
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;
|
||||
|
|
@ -74,11 +85,25 @@ public:
|
|||
FSManipRotateJoint(LLToolComposite* composite);
|
||||
virtual ~FSManipRotateJoint() {}
|
||||
static std::string getManipPartString(EManipPart part);
|
||||
// Called to designate which joint we are going to manipulate.
|
||||
|
||||
/// <summary>
|
||||
/// Sets the joint we are going to manipulate.
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint to interact with.</param>
|
||||
void setJoint(LLJoint* joint);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the avatar the manip should interact with.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to interact with.</param>
|
||||
void setAvatar(LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the reference frame for the manipulator.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The E_PoserReferenceFrame to use.</param>
|
||||
void setReferenceFrame(const E_PoserReferenceFrame frame) { mReferenceFrame = frame; };
|
||||
|
||||
// Overrides
|
||||
void handleSelect() override;
|
||||
bool updateVisiblity() override;
|
||||
|
|
@ -138,8 +163,13 @@ private:
|
|||
void renderRingPass(const RingRenderParams& params, float radius, float width, int pass);
|
||||
void renderAxes(const LLVector3& center, F32 size, const LLQuaternion& rotation);
|
||||
|
||||
bool isAvatarJointSafeToUse();
|
||||
LLQuaternion getSelectedJointWorldRotation();
|
||||
|
||||
float mLastAngle = 0.f;
|
||||
LLVector3 mConstraintAxis;
|
||||
E_PoserReferenceFrame mReferenceFrame = POSER_FRAME_BONE;
|
||||
LLQuaternion mLastSetRotation;
|
||||
};
|
||||
|
||||
#endif // FS_MANIP_ROTATE_JOINT_H
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "fsposeranimator.h"
|
||||
#include "llcharacter.h"
|
||||
#include "llagent.h"
|
||||
#include "llagentcamera.h"
|
||||
#include "fsposingmotion.h"
|
||||
|
||||
std::map<LLUUID, LLAssetID> FSPoserAnimator::sAvatarIdToRegisteredAnimationId;
|
||||
|
|
@ -52,6 +53,25 @@ bool FSPoserAnimator::isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return posingMotion->currentlyPosingJoint(jointPose);
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::hasJointBeenChanged(LLVOAvatar* avatar, const FSPoserJoint& joint)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return false;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return false;
|
||||
|
||||
if (posingMotion->isStopped())
|
||||
return false;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return false;
|
||||
|
||||
return jointPose->getJointModified();
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, bool shouldPose)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
|
|
@ -212,7 +232,8 @@ LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoi
|
|||
return jointPose->getPublicPosition();
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style)
|
||||
void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_PoserReferenceFrame frame,
|
||||
E_BoneDeflectionStyles style)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
|
@ -231,7 +252,8 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
LLVector3 positionDelta = jointPose->getPublicPosition() - position;
|
||||
LLVector3 jointPosition = jointPose->getPublicPosition();
|
||||
LLVector3 positionDelta = jointPosition - position;
|
||||
|
||||
switch (style)
|
||||
{
|
||||
|
|
@ -239,13 +261,13 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
case MIRROR_DELTA:
|
||||
case SYMPATHETIC_DELTA:
|
||||
case SYMPATHETIC:
|
||||
jointPose->setPublicPosition(position);
|
||||
jointPose->setPublicPosition(jointPosition - positionDelta);
|
||||
break;
|
||||
|
||||
case DELTAMODE:
|
||||
case NONE:
|
||||
default:
|
||||
jointPose->setPublicPosition(position);
|
||||
jointPose->setPublicPosition(jointPosition - positionDelta);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +428,7 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
|
|||
return;
|
||||
|
||||
posingMotion->setAllRotationsToZeroAndClearUndo();
|
||||
mPosingState.purgeMotionStates(avatar);
|
||||
|
||||
for (size_t index = 0; index != PoserJoints.size(); ++index)
|
||||
{
|
||||
|
|
@ -420,10 +443,9 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
|
|||
|
||||
posingMotion->setJointBvhLock(jointPose, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation)
|
||||
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
|
@ -440,8 +462,9 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
|
|||
setPosingAvatarJoint(avatar, joint, true);
|
||||
}
|
||||
|
||||
void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
|
||||
E_BoneDeflectionStyles style)
|
||||
void FSPoserAnimator::updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
|
||||
E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, const LLQuaternion rotation,
|
||||
const LLVector3 position, const LLVector3 scale)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
|
@ -454,27 +477,34 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
|
||||
LLQuaternion framedRotation = changeToRotationFrame(avatar, rotation, frame, jointPose);
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * jointPose->getPublicRotation());
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, framedRotation);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
|
||||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion mirroredRotation = LLQuaternion(-framedRotation.mQ[VX], framedRotation.mQ[VY], -framedRotation.mQ[VZ], framedRotation.mQ[VW]);
|
||||
switch (style)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
case SYMPATHETIC_DELTA:
|
||||
oppositeJointPose->cloneRotationFrom(jointPose);
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, framedRotation);
|
||||
break;
|
||||
|
||||
case MIRROR:
|
||||
case MIRROR_DELTA:
|
||||
oppositeJointPose->mirrorRotationFrom(jointPose);
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -482,6 +512,54 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
|
|||
}
|
||||
}
|
||||
|
||||
LLQuaternion FSPoserAnimator::getManipGimbalRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, E_PoserReferenceFrame frame)
|
||||
{
|
||||
LLQuaternion globalRot(-1.f, 0.f, 0.f, 0.f);
|
||||
if (frame == POSER_FRAME_WORLD)
|
||||
return globalRot;
|
||||
|
||||
if (!joint)
|
||||
return globalRot;
|
||||
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return globalRot;
|
||||
|
||||
if (frame == POSER_FRAME_AVATAR)
|
||||
{
|
||||
LLJoint* pelvis = avatar->getJoint("mPelvis");
|
||||
if (pelvis)
|
||||
return pelvis->getWorldRotation();
|
||||
|
||||
return globalRot;
|
||||
}
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return globalRot;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
|
||||
if (!jointPose)
|
||||
return globalRot;
|
||||
|
||||
LLJoint* llJoint = jointPose->getJointState()->getJoint();
|
||||
if (!llJoint)
|
||||
return globalRot;
|
||||
|
||||
if (frame == POSER_FRAME_BONE)
|
||||
return llJoint->getWorldRotation();
|
||||
|
||||
LLVector3 skyward(0.f, 0.f, 1.f);
|
||||
LLVector3 left(1.f, 0.f, 0.f);
|
||||
LLVector3 up, jointToCameraPosition, jointPosition;
|
||||
jointPosition = llJoint->getWorldPosition();
|
||||
jointToCameraPosition = jointPosition - gAgentCamera.getCameraPositionAgent();
|
||||
jointToCameraPosition.normalize();
|
||||
left.setVec(skyward % jointToCameraPosition);
|
||||
up.setVec(jointToCameraPosition % left);
|
||||
|
||||
return LLQuaternion(jointToCameraPosition, left, up);
|
||||
}
|
||||
|
||||
LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const
|
||||
{
|
||||
auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING);
|
||||
|
|
@ -525,12 +603,13 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi
|
|||
if (!jointPose)
|
||||
return vec3;
|
||||
|
||||
return translateRotationFromQuaternion(translation, negation, jointPose->getPublicRotation());
|
||||
return translateRotationFromQuaternion(jointPose, translation, negation, jointPose->getPublicRotation());
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation,
|
||||
const LLVector3& deltaRotation, E_BoneDeflectionStyles deflectionStyle,
|
||||
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle)
|
||||
const LLVector3& deltaRotation, E_BoneDeflectionStyles style, E_PoserReferenceFrame frame,
|
||||
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero,
|
||||
E_RotationStyle rotationStyle)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
|
@ -545,13 +624,15 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation);
|
||||
LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation);
|
||||
switch (deflectionStyle)
|
||||
bool translationRequiresDelta = frame != POSER_FRAME_BONE;
|
||||
|
||||
LLQuaternion absRot = translateRotationToQuaternion(avatar, jointPose, frame, translation, negation, absRotation);
|
||||
LLQuaternion deltaRot = translateRotationToQuaternion(avatar, jointPose, frame, translation, negation, deltaRotation);
|
||||
switch (style)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
case MIRROR:
|
||||
if (rotationStyle == DELTAIC_ROT)
|
||||
if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
|
||||
else
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
|
||||
|
|
@ -570,7 +651,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
|
||||
case NONE:
|
||||
default:
|
||||
if (rotationStyle == DELTAIC_ROT)
|
||||
if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
|
||||
else
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
|
||||
|
|
@ -586,8 +667,8 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
|
||||
switch (deflectionStyle)
|
||||
LLQuaternion mirroredRotation = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
|
||||
switch (style)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
oppositeJointPose->cloneRotationFrom(jointPose);
|
||||
|
|
@ -604,13 +685,13 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
case MIRROR:
|
||||
oppositeJointPose->mirrorRotationFrom(jointPose);
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
|
||||
break;
|
||||
|
||||
case MIRROR_DELTA:
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation());
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -709,8 +790,9 @@ void FSPoserAnimator::flipEntirePose(LLVOAvatar* avatar)
|
|||
}
|
||||
}
|
||||
|
||||
// from the UI to the bone, the inverse translation, the un-swap, the backwards
|
||||
LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation)
|
||||
// from the UI to the bone. Bone rotations we store are relative to the skeleton (or to T-Pose, if you want to visualize).
|
||||
LLQuaternion FSPoserAnimator::translateRotationToQuaternion(LLVOAvatar* avatar, FSJointPose* joint, E_PoserReferenceFrame frame,
|
||||
E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation)
|
||||
{
|
||||
if (negation & NEGATE_ALL)
|
||||
{
|
||||
|
|
@ -728,7 +810,7 @@ LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslatio
|
|||
rotation.mV[VZ] *= -1;
|
||||
}
|
||||
|
||||
LLMatrix3 rot_mat;
|
||||
LLMatrix3 rot_mat;
|
||||
switch (translation)
|
||||
{
|
||||
case SWAP_YAW_AND_ROLL:
|
||||
|
|
@ -761,11 +843,63 @@ LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslatio
|
|||
rot_quat = LLQuaternion(rot_mat) * rot_quat;
|
||||
rot_quat.normalize();
|
||||
|
||||
rot_quat = changeToRotationFrame(avatar, rot_quat, frame, joint);
|
||||
|
||||
return rot_quat;
|
||||
}
|
||||
|
||||
LLQuaternion FSPoserAnimator::changeToRotationFrame(LLVOAvatar* avatar, LLQuaternion rotation, E_PoserReferenceFrame frame, FSJointPose* joint)
|
||||
{
|
||||
if (!joint || !avatar)
|
||||
return rotation;
|
||||
|
||||
LLJoint* pelvis = avatar->getJoint("mPelvis");
|
||||
if (!pelvis)
|
||||
return rotation;
|
||||
|
||||
LLVector3 skyward(0.f, 0.f, 1.f);
|
||||
LLVector3 left(1.f, 0.f, 0.f);
|
||||
LLVector3 forwards(0.f, 1.f, 0.f);
|
||||
LLVector3 up, jointToCameraPosition, jointPosition;
|
||||
LLQuaternion worldRotOfWorld(forwards, left, skyward);
|
||||
LLQuaternion differenceInWorldRot, rotDiffInChildFrame, worldRotOfPelvis, worldRotOfCamera;
|
||||
LLQuaternion worldRotOfThisJoint = joint->getJointState()->getJoint()->getWorldRotation();
|
||||
|
||||
switch (frame)
|
||||
{
|
||||
case POSER_FRAME_WORLD:
|
||||
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfWorld;
|
||||
break;
|
||||
|
||||
case POSER_FRAME_AVATAR:
|
||||
worldRotOfPelvis = pelvis->getWorldRotation();
|
||||
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfPelvis;
|
||||
break;
|
||||
|
||||
case POSER_FRAME_CAMERA:
|
||||
jointPosition = joint->getJointState()->getJoint()->getWorldPosition();
|
||||
jointToCameraPosition = jointPosition - gAgentCamera.getCameraPositionAgent();
|
||||
jointToCameraPosition.normalize();
|
||||
left.setVec(skyward % jointToCameraPosition);
|
||||
up.setVec(jointToCameraPosition % left);
|
||||
|
||||
worldRotOfCamera = LLQuaternion(jointToCameraPosition, left, up);
|
||||
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfCamera;
|
||||
break;
|
||||
|
||||
case POSER_FRAME_BONE:
|
||||
default:
|
||||
return rotation;
|
||||
}
|
||||
|
||||
rotDiffInChildFrame = differenceInWorldRot * rotation * ~differenceInWorldRot;
|
||||
rotDiffInChildFrame.conjugate();
|
||||
|
||||
return rotDiffInChildFrame;
|
||||
}
|
||||
|
||||
// from the bone to the UI; this is the 'forwards' use of the enum
|
||||
LLVector3 FSPoserAnimator::translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const
|
||||
LLVector3 FSPoserAnimator::translateRotationFromQuaternion(FSJointPose* joint, E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const
|
||||
{
|
||||
LLVector3 vec3;
|
||||
|
||||
|
|
@ -833,7 +967,8 @@ LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint&
|
|||
return jointPose->getPublicScale();
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style)
|
||||
void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_PoserReferenceFrame frame,
|
||||
E_BoneDeflectionStyles style)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
|
@ -852,25 +987,46 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->setPublicScale(scale);
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
|
||||
if (!oppositeJointPose)
|
||||
return;
|
||||
LLVector3 jointScale = jointPose->getPublicScale();
|
||||
LLVector3 scaleDelta = jointScale - scale;
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
case MIRROR:
|
||||
case SYMPATHETIC_DELTA:
|
||||
case MIRROR_DELTA:
|
||||
oppositeJointPose->setPublicScale(scale);
|
||||
case SYMPATHETIC_DELTA:
|
||||
case SYMPATHETIC:
|
||||
jointPose->setPublicScale(jointScale - scaleDelta);
|
||||
break;
|
||||
|
||||
case DELTAMODE:
|
||||
case NONE:
|
||||
default:
|
||||
jointPose->setPublicScale(jointScale - scaleDelta);
|
||||
return;
|
||||
}
|
||||
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
|
||||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
LLVector3 oppositeJointScale = oppositeJointPose->getPublicScale();
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case MIRROR:
|
||||
case MIRROR_DELTA:
|
||||
oppositeJointPose->setPublicScale(oppositeJointScale + scaleDelta);
|
||||
break;
|
||||
|
||||
case SYMPATHETIC_DELTA:
|
||||
case SYMPATHETIC:
|
||||
oppositeJointPose->setPublicScale(oppositeJointScale - scaleDelta);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos,
|
||||
|
|
@ -914,7 +1070,7 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
|
|||
|
||||
jointPose->purgeUndoQueue();
|
||||
|
||||
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
|
||||
LLQuaternion rot = translateRotationToQuaternion(avatar, jointPose, POSER_FRAME_BONE, SWAP_NOTHING, NEGATE_NOTHING, rotation);
|
||||
jointPose->setPublicRotation(setBaseToZero, rot);
|
||||
}
|
||||
|
||||
|
|
@ -963,13 +1119,21 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
|
|||
}
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::loadPosingState(LLVOAvatar* avatar, LLSD pose)
|
||||
bool FSPoserAnimator::loadPosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return false;
|
||||
|
||||
mPosingState.purgeMotionStates(avatar);
|
||||
mPosingState.restoreMotionStates(avatar, pose);
|
||||
mPosingState.restoreMotionStates(avatar, ignoreOwnership, pose);
|
||||
|
||||
return applyStatesToPosingMotion(avatar);
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::applyStatesToPosingMotion(LLVOAvatar* avatar)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return false;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
|
|
@ -1007,9 +1171,9 @@ void FSPoserAnimator::applyJointMirrorToBaseRotations(FSPosingMotion* posingMoti
|
|||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::savePosingState(LLVOAvatar* avatar, LLSD* saveRecord)
|
||||
void FSPoserAnimator::savePosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord)
|
||||
{
|
||||
mPosingState.writeMotionStates(avatar, saveRecord);
|
||||
mPosingState.writeMotionStates(avatar, ignoreOwnership, saveRecord);
|
||||
}
|
||||
|
||||
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const
|
||||
|
|
@ -1023,6 +1187,39 @@ const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByNumber(LLVOAvatar* avatar, const int jointNumber) const
|
||||
{
|
||||
if (!avatar)
|
||||
return nullptr;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return nullptr;
|
||||
|
||||
FSJointPose* parentJoint = posingMotion->getJointPoseByJointNumber(jointNumber);
|
||||
if (!parentJoint)
|
||||
return nullptr;
|
||||
|
||||
return getPoserJointByName(parentJoint->jointName());
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::tryGetJointNumber(LLVOAvatar* avatar, const FSPoserJoint &poserJoint, int &jointNumber)
|
||||
{
|
||||
if (!avatar)
|
||||
return false;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return false;
|
||||
|
||||
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(poserJoint.jointName());
|
||||
if (!parentJoint)
|
||||
return false;
|
||||
|
||||
jointNumber = parentJoint->getJointNumber();
|
||||
return jointNumber >= 0;
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
|
|
@ -1038,7 +1235,6 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
|
|||
gAgent.stopFidget();
|
||||
|
||||
mPosingState.captureMotionStates(avatar);
|
||||
|
||||
avatar->startDefaultMotions();
|
||||
avatar->startMotion(posingMotion->motionId());
|
||||
|
||||
|
|
@ -1057,11 +1253,17 @@ void FSPoserAnimator::updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserA
|
|||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
std::string jointNamesRecaptured;
|
||||
std::vector<int> jointNumbersRecaptured;
|
||||
for (auto item : jointsRecaptured)
|
||||
jointNamesRecaptured += item->jointName();
|
||||
{
|
||||
auto poserJoint = posingMotion->getJointPoseByJointName(item->jointName());
|
||||
if (!poserJoint)
|
||||
continue;
|
||||
|
||||
mPosingState.updateMotionStates(avatar, posingMotion, jointNamesRecaptured);
|
||||
jointNumbersRecaptured.push_back(poserJoint->getJointNumber());
|
||||
}
|
||||
|
||||
mPosingState.updateMotionStates(avatar, posingMotion, jointNumbersRecaptured);
|
||||
}
|
||||
|
||||
void FSPoserAnimator::stopPosingAvatar(LLVOAvatar *avatar)
|
||||
|
|
@ -1219,6 +1421,9 @@ void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint
|
|||
|
||||
void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
|
||||
{
|
||||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "fsposingmotion.h"
|
||||
#include "fsposestate.h"
|
||||
#include "llvoavatar.h"
|
||||
#include "fsmaniprotatejoint.h"
|
||||
|
||||
/// <summary>
|
||||
/// Describes how we will cluster the joints/bones/thingos.
|
||||
|
|
@ -395,6 +396,20 @@ public:
|
|||
/// <returns>The matching joint if found, otherwise nullptr</returns>
|
||||
const FSPoserJoint* getPoserJointByName(const std::string& jointName) const;
|
||||
|
||||
/// <summary>
|
||||
/// Get a PoserJoint case-insensitive-matching the supplied name.
|
||||
/// </summary>
|
||||
/// <param name="jointNumber">The name of the joint to match.</param>
|
||||
/// <returns>The matching joint if found, otherwise nullptr</returns>
|
||||
const FSPoserJoint* getPoserJointByNumber(LLVOAvatar* avatar, const int jointNumber) const;
|
||||
|
||||
/// <summary>
|
||||
/// Get a PoserJoint by its LLJoint number.
|
||||
/// </summary>
|
||||
/// <param name="jointNumber">The name of the joint to match.</param>
|
||||
/// <returns>The matching joint if found, otherwise nullptr</returns>
|
||||
bool tryGetJointNumber(LLVOAvatar* avatar, const FSPoserJoint &poserJoint, int &jointNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to start posing the supplied avatar.
|
||||
/// </summary>
|
||||
|
|
@ -423,6 +438,14 @@ public:
|
|||
/// <returns>True if this is joint is being posed for the supplied avatar, otherwise false.</returns>
|
||||
bool isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the supplied PoserJoint for the supplied avatar has been modified this session, even if all change has been reverted.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar having the joint to which we refer.</param>
|
||||
/// <param name="joint">The joint being queried for.</param>
|
||||
/// <returns>True if this is joint has been changed while posing even if the change has been reverted or undone, otherwise false.</returns>
|
||||
bool hasJointBeenChanged(LLVOAvatar* avatar, const FSPoserJoint& joint);
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the supplied PoserJoint for the supplied avatar should be posed.
|
||||
/// </summary>
|
||||
|
|
@ -479,8 +502,10 @@ public:
|
|||
/// <param name="avatar">The avatar whose joint is to be set.</param>
|
||||
/// <param name="joint">The joint to set.</param>
|
||||
/// <param name="position">The position to set the joint to.</param>
|
||||
/// <param name="frame">The frame to translate the position to.</param>
|
||||
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
|
||||
void setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style);
|
||||
void setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_PoserReferenceFrame frame,
|
||||
E_BoneDeflectionStyles style);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation of a joint for the supplied avatar.
|
||||
|
|
@ -506,6 +531,15 @@ public:
|
|||
/// </remarks>
|
||||
LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation suitable for the Manip gimbal for the supplied avatar and joint.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar having the Manip gimbal placed upon it.</param>
|
||||
/// <param name="joint">The joint on the avatar where the manip should be placed.</param>
|
||||
/// <param name="frame">The frame of reference for the gimbal.</param>
|
||||
/// <returns>The rotation to set the gimbal to.</returns>
|
||||
LLQuaternion getManipGimbalRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, E_PoserReferenceFrame frame);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the rotation of a joint for the supplied avatar.
|
||||
/// </summary>
|
||||
|
|
@ -518,8 +552,9 @@ public:
|
|||
/// <param name="negation">The style of negation to apply to the set.</param>
|
||||
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
|
||||
/// <param name="rotationStyle">Whether to apply the supplied rotation as a delta to the supplied joint.</param>
|
||||
void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, const LLVector3& deltaRotation, E_BoneDeflectionStyles style,
|
||||
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle);
|
||||
void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, const LLVector3& deltaRotation,
|
||||
E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, E_BoneAxisTranslation translation, S32 negation,
|
||||
bool resetBaseRotationToZero, E_RotationStyle rotationStyle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scale of a joint for the supplied avatar.
|
||||
|
|
@ -535,8 +570,10 @@ public:
|
|||
/// <param name="avatar">The avatar whose joint is to be set.</param>
|
||||
/// <param name="joint">The joint to set.</param>
|
||||
/// <param name="scale">The scale to set the joint to.</param>
|
||||
/// <param name="frame">The frame to translate the position to.</param>
|
||||
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
|
||||
void setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style);
|
||||
void setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_PoserReferenceFrame frame,
|
||||
E_BoneDeflectionStyles style);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the joint with its opposite if it has one, or just mirror the rotation of itself.
|
||||
|
|
@ -564,9 +601,7 @@ public:
|
|||
/// </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 recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint);
|
||||
|
||||
/// <summary>
|
||||
/// Recaptures any change in joint state.
|
||||
|
|
@ -575,7 +610,11 @@ public:
|
|||
/// <param name="joint">The joint to recapture.</param>
|
||||
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
|
||||
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
|
||||
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style);
|
||||
/// <param name="rotation">The rotation of the supplied joint.</param>
|
||||
/// <param name="position">The position of the supplied joint.</param>
|
||||
/// <param name="scale">The scale of the supplied joint.</param>
|
||||
void updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style,
|
||||
E_PoserReferenceFrame frame, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale);
|
||||
|
||||
/// <summary>
|
||||
/// Sets all of the joint rotations of the supplied avatar to zero.
|
||||
|
|
@ -711,6 +750,7 @@ public:
|
|||
/// Loads the posing state (base rotations) to the supplied avatars posing-motion, from the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">That avatar whose posing state should be loaded.</param>
|
||||
/// <param name="ignoreOwnership">Whether to ignore ownership. For use when reading a local file.</param>
|
||||
/// <param name="pose">The record to read the posing state from.</param>
|
||||
/// <returns>True if the pose loaded successfully, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
|
|
@ -718,14 +758,22 @@ public:
|
|||
/// it can take several frames for the animation to be loaded and ready.
|
||||
/// It may therefore be necessary to attempt this several times.
|
||||
/// </remarks>
|
||||
bool loadPosingState(LLVOAvatar* avatar, LLSD pose);
|
||||
bool loadPosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the posing states to the posing motion for the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">That avatar whose posing state should be loaded.</param>
|
||||
/// <returns>True if the state applied successfully, otherwise false.</returns>
|
||||
bool applyStatesToPosingMotion(LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the posing state for the supplied avatar to the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">That avatar whose posing state should be written.</param>
|
||||
/// <param name="ignoreOwnership">Whether to ignore ownership while saving.</param>
|
||||
/// <param name="saveRecord">The record to write the posing state to.</param>
|
||||
void savePosingState(LLVOAvatar* avatar, LLSD* saveRecord);
|
||||
void savePosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord);
|
||||
|
||||
/// <summary>
|
||||
/// Purges and recaptures the pose state for the supplied avatar.
|
||||
|
|
@ -734,17 +782,6 @@ public:
|
|||
/// <param name="jointsRecaptured">The joints which were recaptured.</param>
|
||||
void updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserAnimator::FSPoserJoint*> jointsRecaptured);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new posing state, or updates the matching posing state with the supplied data.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar the posing state is intended for.</param>
|
||||
/// <param name="animId">The ID of the animation.</param>
|
||||
/// <param name="updateTime">The frame-time of the animation.</param>
|
||||
/// <param name="jointNames">The names of the joints, if any, the animation should specifically be applied to.</param>
|
||||
/// <param name="captureOrder">The capture order.</param>
|
||||
/// <returns>True if the posing state was added or changed by the update data, otherwise false.</returns>
|
||||
bool addOrUpdatePosingState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder);
|
||||
|
||||
/// <summary>
|
||||
/// Traverses the joints and applies reversals to the base rotations if needed.
|
||||
/// </summary>
|
||||
|
|
@ -755,14 +792,19 @@ public:
|
|||
void applyJointMirrorToBaseRotations(FSPosingMotion* posingMotion);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Translates a rotation vector from the UI to a Quaternion for the bone.
|
||||
/// This also performs the axis-swapping the UI needs for up/down/left/right to make sense.
|
||||
/// </summary>
|
||||
/// <param name="translation">The axis translation to perform.</param>
|
||||
/// <param name="rotation">The rotation to transform to quaternion.</param>
|
||||
/// <returns>The rotation quaternion.</returns>
|
||||
LLQuaternion translateRotationToQuaternion(E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation);
|
||||
/// <summary>
|
||||
/// Translates the supplied rotation vector from UI to a Quaternion for the bone.
|
||||
/// Also performs the axis-swapping and other transformations for up/down/left/right to make sense.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose joint is being manipulated.</param>
|
||||
/// <param name="joint">The joint which is being altered.</param>
|
||||
/// <param name="frame">The frame of reference the translation should be performed in.</param>
|
||||
/// <param name="translation">The axis translation to perform.</param>
|
||||
/// <param name="negation">The style of axis-negation.</param>
|
||||
/// <param name="rotation">The rotation to translate and transform to quaternion.</param>
|
||||
/// <returns>The translated rotation quaternion.</returns>
|
||||
LLQuaternion translateRotationToQuaternion(LLVOAvatar* avatar, FSJointPose* joint, E_PoserReferenceFrame frame,
|
||||
E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Translates a bone-rotation quaternion to a vector usable easily on the UI.
|
||||
|
|
@ -770,7 +812,7 @@ public:
|
|||
/// <param name="translation">The axis translation to perform.</param>
|
||||
/// <param name="rotation">The rotation to transform to matrix.</param>
|
||||
/// <returns>The rotation vector.</returns>
|
||||
LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const;
|
||||
LLVector3 translateRotationFromQuaternion(FSJointPose* joint, E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a posing motion for the supplied avatar.
|
||||
|
|
@ -844,6 +886,20 @@ public:
|
|||
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
|
||||
void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the supplied rotation into the desired frame.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="rotation">The rotation to convert.</param>
|
||||
/// <param name="frame">The frame to translate the rotation to.</param>
|
||||
/// <param name="joint">The joint whose rotation is being changed.</param>
|
||||
/// <remarks>
|
||||
/// Input rotations have no implicit frame: it's just a rotation and ordinarily applied, inherits the joint's rotational framing.
|
||||
/// This method imposes a framing upon the supplied rotation, meaning user input is considered as relative to something like
|
||||
/// 'the world', 'avatar pelvis' or the position of the camera relative to the joint.
|
||||
/// </remarks>
|
||||
LLQuaternion changeToRotationFrame(LLVOAvatar* avatar, LLQuaternion rotation, E_PoserReferenceFrame frame, FSJointPose* joint);
|
||||
|
||||
/// <summary>
|
||||
/// Maps the avatar's ID to the animation registered to them.
|
||||
/// Thus we start/stop the same animation, and get/set the same rotations etc.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
std::map<LLUUID, std::vector<FSPoseState::fsMotionState>> FSPoseState::sMotionStates;
|
||||
std::map<LLUUID, int> FSPoseState::sCaptureOrder;
|
||||
std::map<LLUUID, bool> FSPoseState::sMotionStatesOwnedByMe;
|
||||
|
||||
void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
|
||||
{
|
||||
|
|
@ -10,6 +11,7 @@ void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
|
|||
return;
|
||||
|
||||
sCaptureOrder[avatar->getID()] = 0;
|
||||
int animNumber = 0;
|
||||
|
||||
for (auto anim_it = avatar->mPlayingAnimations.begin(); anim_it != avatar->mPlayingAnimations.end(); ++anim_it)
|
||||
{
|
||||
|
|
@ -21,27 +23,26 @@ void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
|
|||
newState.motionId = anim_it->first;
|
||||
newState.lastUpdateTime = motion->getLastUpdateTime();
|
||||
newState.captureOrder = 0;
|
||||
newState.avatarOwnsPose = canSaveMotionId(avatar, anim_it->first);
|
||||
newState.inLayerOrder = animNumber++;
|
||||
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured)
|
||||
void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::vector<S32> jointNumbersRecaptured)
|
||||
{
|
||||
if (!avatar || !posingMotion)
|
||||
return;
|
||||
|
||||
sCaptureOrder[avatar->getID()]++;
|
||||
int animNumber = 0;
|
||||
|
||||
// if an animation for avatar is a subset of jointNamesRecaptured, delete it
|
||||
// if an animation for avatar is a subset of jointNumbersRecaptured, delete it
|
||||
// this happens on second/subsequent recaptures; the first recapture is no longer needed
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end();)
|
||||
{
|
||||
std::string joints = (*it).jointNamesAnimated;
|
||||
bool recaptureMatches = !joints.empty() && !jointNamesRecaptured.empty() && jointNamesRecaptured.find(joints) != std::string::npos;
|
||||
|
||||
if (recaptureMatches)
|
||||
if (vector2IsSubsetOfVector1(jointNumbersRecaptured, (*it).jointNumbersAnimated))
|
||||
it = sMotionStates[avatar->getID()].erase(it);
|
||||
else
|
||||
it++;
|
||||
|
|
@ -53,7 +54,7 @@ void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingM
|
|||
if (!motion)
|
||||
continue;
|
||||
|
||||
if (!posingMotion->otherMotionAnimatesJoints(motion, jointNamesRecaptured))
|
||||
if (!posingMotion->otherMotionAnimatesJoints(motion, jointNumbersRecaptured))
|
||||
continue;
|
||||
|
||||
bool foundMatch = false;
|
||||
|
|
@ -71,45 +72,17 @@ void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingM
|
|||
continue;
|
||||
|
||||
fsMotionState newState;
|
||||
newState.motionId = anim_it->first;
|
||||
newState.lastUpdateTime = motion->getLastUpdateTime();
|
||||
newState.jointNamesAnimated = jointNamesRecaptured;
|
||||
newState.captureOrder = sCaptureOrder[avatar->getID()];
|
||||
newState.avatarOwnsPose = canSaveMotionId(avatar, anim_it->first);
|
||||
newState.motionId = anim_it->first;
|
||||
newState.lastUpdateTime = motion->getLastUpdateTime();
|
||||
newState.jointNumbersAnimated = jointNumbersRecaptured;
|
||||
newState.captureOrder = sCaptureOrder[avatar->getID()];
|
||||
newState.inLayerOrder = animNumber++;
|
||||
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPoseState::addOrUpdatePosingMotionState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder)
|
||||
{
|
||||
if (!avatar)
|
||||
return false;
|
||||
|
||||
bool foundMatch = false;
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
|
||||
{
|
||||
bool motionIdMatches = (*it).motionId == animId;
|
||||
bool updateTimesMatch = (*it).lastUpdateTime == updateTime;
|
||||
bool jointNamesMatch = (*it).jointNamesAnimated == jointNames;
|
||||
bool captureOrdersMatch = (*it).captureOrder == captureOrder;
|
||||
|
||||
foundMatch = motionIdMatches && updateTimesMatch && jointNamesMatch && captureOrdersMatch;
|
||||
if (foundMatch)
|
||||
return false;
|
||||
}
|
||||
|
||||
fsMotionState newState;
|
||||
newState.motionId = animId;
|
||||
newState.lastUpdateTime = updateTime;
|
||||
newState.jointNamesAnimated = jointNames;
|
||||
newState.captureOrder = captureOrder;
|
||||
newState.avatarOwnsPose = false;
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
|
||||
{
|
||||
if (!avatar)
|
||||
|
|
@ -118,7 +91,7 @@ void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
|
|||
sMotionStates[avatar->getID()].clear();
|
||||
}
|
||||
|
||||
void FSPoseState::writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord)
|
||||
void FSPoseState::writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
|
@ -126,18 +99,27 @@ void FSPoseState::writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord)
|
|||
int animNumber = 0;
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); ++it)
|
||||
{
|
||||
if (!it->avatarOwnsPose)
|
||||
continue;
|
||||
if (!ignoreOwnership && !it->gAgentOwnsPose)
|
||||
{
|
||||
if (it->requeriedAssetInventory)
|
||||
continue;
|
||||
|
||||
std::string uniqueAnimId = "poseState" + std::to_string(animNumber++);
|
||||
(*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString();
|
||||
(*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime;
|
||||
(*saveRecord)[uniqueAnimId]["jointNamesAnimated"] = it->jointNamesAnimated;
|
||||
(*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder;
|
||||
it->gAgentOwnsPose = canSaveMotionId(avatar, it->motionId);
|
||||
it->requeriedAssetInventory = true;
|
||||
if (!it->gAgentOwnsPose)
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string uniqueAnimId = "poseState" + std::to_string(animNumber++);
|
||||
(*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString();
|
||||
(*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime;
|
||||
(*saveRecord)[uniqueAnimId]["jointNumbersAnimated"] = encodeVectorToString(it->jointNumbersAnimated);
|
||||
(*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder;
|
||||
(*saveRecord)[uniqueAnimId]["inLayerOrder"] = it->inLayerOrder;
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, LLSD pose)
|
||||
void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
|
@ -153,25 +135,33 @@ void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, LLSD pose)
|
|||
continue;
|
||||
|
||||
fsMotionState newState;
|
||||
newState.avatarOwnsPose = true;
|
||||
|
||||
if (control_map.has("animationId"))
|
||||
{
|
||||
std::string const name = control_map["animationId"].asString();
|
||||
LLUUID animId;
|
||||
if (LLUUID::parseUUID(name, &animId))
|
||||
{
|
||||
newState.motionId = animId;
|
||||
newState.gAgentOwnsPose = ignoreOwnership || canSaveMotionId(avatar, animId);
|
||||
|
||||
if (ignoreOwnership)
|
||||
sMotionStatesOwnedByMe[animId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (control_map.has("lastUpdateTime"))
|
||||
newState.lastUpdateTime = (F32)control_map["lastUpdateTime"].asReal();
|
||||
|
||||
if (control_map.has("jointNamesAnimated"))
|
||||
newState.jointNamesAnimated = control_map["jointNamesAnimated"].asString();
|
||||
if (control_map.has("jointNumbersAnimated"))
|
||||
newState.jointNumbersAnimated = decodeStringToVector(control_map["jointNumbersAnimated"].asString());
|
||||
|
||||
if (control_map.has("captureOrder"))
|
||||
newState.captureOrder = control_map["captureOrder"].asInteger();
|
||||
|
||||
if (control_map.has("inLayerOrder"))
|
||||
newState.inLayerOrder = control_map["inLayerOrder"].asInteger();
|
||||
|
||||
if (newState.captureOrder > sCaptureOrder[avatar->getID()])
|
||||
sCaptureOrder[avatar->getID()] = newState.captureOrder;
|
||||
|
||||
|
|
@ -207,7 +197,7 @@ bool FSPoseState::applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMo
|
|||
resetPriorityForCaptureOrder(avatar, posingMotion, lastCaptureOrder);
|
||||
}
|
||||
|
||||
it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNamesAnimated);
|
||||
it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNumbersAnimated);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -225,26 +215,46 @@ void FSPoseState::resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotio
|
|||
{
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
|
||||
{
|
||||
if (it->jointNamesAnimated.empty())
|
||||
if (it->jointNumbersAnimated.empty())
|
||||
continue;
|
||||
if (it->motionApplied)
|
||||
continue;
|
||||
if (it->captureOrder != captureOrder)
|
||||
continue;
|
||||
|
||||
posingMotion->resetBonePriority(it->jointNamesAnimated);
|
||||
posingMotion->resetBonePriority(it->jointNumbersAnimated);
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPoseState::canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId)
|
||||
bool FSPoseState::canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
|
||||
{
|
||||
if (!gAgentAvatarp || gAgentAvatarp.isNull())
|
||||
return false;
|
||||
|
||||
if (sMotionStatesOwnedByMe[motionId])
|
||||
return true;
|
||||
|
||||
// does the animation exist in inventory
|
||||
LLInventoryItem* item = gInventory.getItem(motionId);
|
||||
if (item && item->getPermissions().getOwner() == avatar->getID())
|
||||
return true;
|
||||
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
|
||||
{
|
||||
sMotionStatesOwnedByMe[motionId] = true;
|
||||
return sMotionStatesOwnedByMe[motionId];
|
||||
}
|
||||
|
||||
if (!avatarPlayingMotionId)
|
||||
return false;
|
||||
|
||||
if (avatarPlayingMotionId->getID() == gAgentAvatarp->getID())
|
||||
return motionIdIsAgentAnimationSource(motionId);
|
||||
|
||||
return motionIdIsFromPrimAgentOwnsAgentIsSittingOn(avatarPlayingMotionId, motionId);
|
||||
}
|
||||
|
||||
bool FSPoseState::motionIdIsAgentAnimationSource(LLAssetID motionId)
|
||||
{
|
||||
if (!gAgentAvatarp || gAgentAvatarp.isNull())
|
||||
return false;
|
||||
|
||||
for (const auto& [anim_object_id, anim_anim_id] : gAgentAvatarp->mAnimationSources)
|
||||
{
|
||||
|
|
@ -252,17 +262,150 @@ bool FSPoseState::canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId)
|
|||
continue;
|
||||
|
||||
// is the item that started the anim in inventory
|
||||
item = gInventory.getItem(anim_object_id);
|
||||
if (item && item->getPermissions().getOwner() == avatar->getID())
|
||||
return true;
|
||||
LLInventoryItem* item = gInventory.getItem(anim_object_id);
|
||||
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
|
||||
{
|
||||
sMotionStatesOwnedByMe[motionId] = true;
|
||||
return sMotionStatesOwnedByMe[motionId];
|
||||
}
|
||||
|
||||
// is the item that start the animation in-world
|
||||
LLViewerObject* object = gObjectList.findObject(anim_object_id);
|
||||
if (object && object->permYouOwner())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
{
|
||||
sMotionStatesOwnedByMe[motionId] = true;
|
||||
return sMotionStatesOwnedByMe[motionId];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FSPoseState::motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
|
||||
{
|
||||
if (!avatarPlayingMotionId)
|
||||
return false;
|
||||
|
||||
const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(avatarPlayingMotionId->getRoot());
|
||||
if (!agentRoot)
|
||||
return false;
|
||||
|
||||
const LLUUID& assetIdTheyAreSittingOn = agentRoot->getID();
|
||||
if (assetIdTheyAreSittingOn == avatarPlayingMotionId->getID())
|
||||
return false; // they are not sitting on a thing
|
||||
|
||||
LLViewerObject* object = gObjectList.findObject(assetIdTheyAreSittingOn);
|
||||
if (!object || !object->permYouOwner())
|
||||
return false; // gAgent does not own what they are sitting on
|
||||
|
||||
if (object->isInventoryPending())
|
||||
return false;
|
||||
|
||||
if (object->isInventoryDirty() || !object->getInventoryRoot())
|
||||
{
|
||||
object->requestInventory();
|
||||
return false; // whatever they are sitting on, we don't have the inventory list for yet
|
||||
}
|
||||
|
||||
LLInventoryItem* item = object->getInventoryItemByAsset(motionId, LLAssetType::AT_ANIMATION);
|
||||
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
|
||||
{
|
||||
sMotionStatesOwnedByMe[motionId] = true;
|
||||
return sMotionStatesOwnedByMe[motionId];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FSPoseState::vector2IsSubsetOfVector1(std::vector<S32> newRecapture, std::vector<S32> oldRecapture)
|
||||
{
|
||||
if (newRecapture.size() < 1)
|
||||
return false;
|
||||
if (oldRecapture.size() < 1)
|
||||
return false;
|
||||
|
||||
if (newRecapture.size() < oldRecapture.size())
|
||||
return false;
|
||||
|
||||
for (S32 number : oldRecapture)
|
||||
if (std::find(newRecapture.begin(), newRecapture.end(), number) == newRecapture.end())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string FSPoseState::encodeVectorToString(std::vector<S32> vector)
|
||||
{
|
||||
std::string encoded = "";
|
||||
if (vector.size() < 1)
|
||||
return encoded;
|
||||
|
||||
for (S32 numberToEncode : vector)
|
||||
{
|
||||
if (numberToEncode > 251) // max 216 at time of writing
|
||||
continue;
|
||||
|
||||
S32 number = numberToEncode;
|
||||
|
||||
if (number >= 189)
|
||||
{
|
||||
encoded += "~";
|
||||
number -= 189;
|
||||
}
|
||||
|
||||
if (number >= 126)
|
||||
{
|
||||
encoded += "}";
|
||||
number -= 126;
|
||||
}
|
||||
|
||||
if (number >= 63)
|
||||
{
|
||||
encoded += "|";
|
||||
number -= 63;
|
||||
}
|
||||
|
||||
encoded += char(number + int('?'));
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::vector<S32> FSPoseState::decodeStringToVector(std::string vector)
|
||||
{
|
||||
std::vector<S32> decoded;
|
||||
if (vector.empty())
|
||||
return decoded;
|
||||
|
||||
S32 number = 0;
|
||||
for (char ch : vector)
|
||||
{
|
||||
if (ch > '~' || ch < '?')
|
||||
continue;
|
||||
|
||||
if (ch == '~')
|
||||
{
|
||||
number += 189;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '}')
|
||||
{
|
||||
number += 126;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '|')
|
||||
{
|
||||
number += 63;
|
||||
continue;
|
||||
}
|
||||
|
||||
number -= int('?');
|
||||
number += S32(ch);
|
||||
decoded.push_back(number);
|
||||
number = 0;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,18 +49,7 @@ public:
|
|||
/// <param name="avatar">The avatar whose animations are to be captured.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="jointNamesRecaptured">The names of the joints being recaptured.</param>
|
||||
void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new posing state, or updates the matching posing state with the supplied data.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar the posing state is intended for.</param>
|
||||
/// <param name="animId">The ID of the animation.</param>
|
||||
/// <param name="updateTime">The frame-time of the animation.</param>
|
||||
/// <param name="jointNames">The names of the joints, if any, the animation should specifically be applied to.</param>
|
||||
/// <param name="captureOrder">The capture order.</param>
|
||||
/// <returns>True if the posing state was added or changed by the update data, otherwise false.</returns>
|
||||
bool addOrUpdatePosingMotionState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder);
|
||||
void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::vector<S32> jointNamesRecaptured);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all current animation states for the supplied avatar.
|
||||
|
|
@ -72,16 +61,17 @@ public:
|
|||
/// Writes any documented poses for the supplied avatar to the supplied stream.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations may have been captured.</param>
|
||||
/// <param name="ignoreOwnership">Whether to ignore ownership. For use when preparing saveRecord to send to another by collab.</param>
|
||||
/// <param name="saveRecord">The record to add to.</param>
|
||||
void writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord);
|
||||
void writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord);
|
||||
|
||||
/// <summary>
|
||||
/// Restores pose state(s) from the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations may have been captured.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="ignoreOwnership">Whether to ignore ownership. For use when reading a local file.</param>
|
||||
/// <param name="pose">The record to read from.</param>
|
||||
void restoreMotionStates(LLVOAvatar* avatar, LLSD pose);
|
||||
void restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the motion states for the supplied avatar to the supplied motion.
|
||||
|
|
@ -122,20 +112,30 @@ private:
|
|||
bool motionApplied = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the avatar owns the pose, or the pose was loaded.
|
||||
/// For non-gAgent, we permit a query of the inventory of a prim they are sitting on.
|
||||
/// Because this involves latency, we may retry ownership checking at save-time.
|
||||
/// </summary>
|
||||
bool avatarOwnsPose = false;
|
||||
bool requeriedAssetInventory = false;
|
||||
|
||||
/// <summary>
|
||||
/// When reloading, larger numbers are loaded last, nesting order and priority.
|
||||
/// This is used to represent recaptures, where joints could be animated with different poses.
|
||||
/// Whether gAgent owns the pose, or the pose was loaded from XML.
|
||||
/// </summary>
|
||||
bool gAgentOwnsPose = false;
|
||||
|
||||
/// <summary>
|
||||
/// Represents 'capture layers: how the user layers animations 'on top of' others.
|
||||
/// </summary>
|
||||
int captureOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// When reloading, and if not-empty, the names of the bones this motionId should affect.
|
||||
/// Represents in-layer order of capture.
|
||||
/// </summary>
|
||||
std ::string jointNamesAnimated;
|
||||
int inLayerOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// When reloading, and if not-empty, the bone-numbers this motionId should affect.
|
||||
/// </summary>
|
||||
std ::vector<S32> jointNumbersAnimated;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -147,12 +147,58 @@ private:
|
|||
void resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, int captureOrder);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied avatar owns, and thus can save information about the supplied asset ID.
|
||||
/// Gets whether gAgentID owns, and thus can save information about the supplied motionId.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to query ownership for.</param>
|
||||
/// <param name="motionId">The asset ID of the object.</param>
|
||||
/// <returns>True if the avatar owns the asset, otherwise false.</returns>
|
||||
bool canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId);
|
||||
/// <param name="avatarPlayingMotionId">The avatar playing the supplied motionId.</param>
|
||||
/// <param name="motionId">The motionId of the animation.</param>
|
||||
/// <returns>True if the gAgent owns the motionId, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// This only works reliably for self.
|
||||
/// For motions playing on others, the motion needs to be an asset in gAgent's inventory.
|
||||
/// </remarks>
|
||||
bool canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId);
|
||||
|
||||
/// <summary>
|
||||
/// Examines gAgent's animation source list for the supplied animation Id.
|
||||
/// </summary>
|
||||
/// <param name="motionId">The ID of the motion to query.</param>
|
||||
/// <returns>True if gAgent is playing the animation, otherwise false.</returns>
|
||||
bool motionIdIsAgentAnimationSource(LLAssetID motionId);
|
||||
|
||||
/// <summary>
|
||||
/// Queries a specific condition of the supplied animation ID.
|
||||
/// </summary>
|
||||
/// <param name="avatarPlayingMotionId">The avatar to query for.</param>
|
||||
/// <param name="motionId">The motion ID to query for.</param>
|
||||
/// <returns>
|
||||
/// True if the supplied avatar is sitting on an object owned by gAgent, and that object
|
||||
/// contains an animation asset with the same assetId.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This is intended to test for a situation a photographer might arrange.
|
||||
/// If you are sitting on photographer's prim, playing photographer's pose, and photographer wants to save their work,
|
||||
/// this allows them to save the Animation ID and state to XML.
|
||||
/// It is intended this be called twice at least, as it does not implement a callback onInventoryLoaded.
|
||||
/// Presently this works fine: first time being when posing starts, second when pose is saved.
|
||||
/// </remarks>
|
||||
bool motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId);
|
||||
|
||||
/// <summary>
|
||||
/// Tests if all the members of supplied vector2 are members of supplied vector1.
|
||||
/// </summary>
|
||||
/// <param name="vector1">The super-set.</param>
|
||||
/// <param name="vector2">The possible sub-set.</param>
|
||||
/// <returns>True if all members of vector2 are members of vector1, otherwise false.</returns>
|
||||
bool vector2IsSubsetOfVector1(std::vector<S32> vector1, std::vector<S32> vector2);
|
||||
|
||||
/// <summary>
|
||||
/// Two symmetric methods for (de)serializing vectors to both XML and collab-safe short-as-possible strings and back again.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Collab-safe means ASCII-printable chars, and delimiter usage does not conflict with Collab's delimiter.
|
||||
/// </remarks>
|
||||
std::string encodeVectorToString(std::vector<S32> vector);
|
||||
std::vector<S32> decodeStringToVector(std::string vector);
|
||||
|
||||
struct compareByCaptureOrder
|
||||
{
|
||||
|
|
@ -160,6 +206,8 @@ private:
|
|||
{
|
||||
if (a.captureOrder < b.captureOrder)
|
||||
return true; // Ascending order
|
||||
if (a.captureOrder == b.captureOrder && a.inLayerOrder < b.inLayerOrder)
|
||||
return true; // Ascending order in layer
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -167,6 +215,7 @@ private:
|
|||
|
||||
static std::map <LLUUID, std::vector<fsMotionState>> sMotionStates;
|
||||
static std::map<LLUUID, int> sCaptureOrder;
|
||||
static std::map<LLUUID, bool> sMotionStatesOwnedByMe;
|
||||
};
|
||||
|
||||
#endif // LL_FSPoseState_H
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ void FSPosingMotion::setJointState(LLJoint* joint, U32 state)
|
|||
|
||||
FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
|
||||
{
|
||||
if (mJointPoses.size() < 1)
|
||||
if (name.empty() || mJointPoses.size() < 1)
|
||||
return nullptr;
|
||||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
|
|
@ -222,6 +222,24 @@ FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
FSJointPose* FSPosingMotion::getJointPoseByJointNumber(const S32& number)
|
||||
{
|
||||
if (mJointPoses.size() < 1)
|
||||
return nullptr;
|
||||
if (number < 0)
|
||||
return nullptr;
|
||||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
{
|
||||
if (poserJoint_iter->getJointNumber() != number)
|
||||
continue;
|
||||
|
||||
return &*poserJoint_iter;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint)
|
||||
{
|
||||
if (mJointPoses.size() < 1)
|
||||
|
|
@ -270,14 +288,14 @@ void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
|
|||
joint->zeroBaseRotation(lockInBvh);
|
||||
}
|
||||
|
||||
bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames)
|
||||
bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::vector<S32> selectedJointNumbers)
|
||||
{
|
||||
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToLoad);
|
||||
if (!motionToLoadAsFsPosingMotion)
|
||||
return false;
|
||||
|
||||
LLJoint::JointPriority priority = motionToLoad->getPriority();
|
||||
bool motionIsForAllJoints = selectedJointNames.empty();
|
||||
bool motionIsForAllJoints = selectedJointNumbers.empty();
|
||||
|
||||
LLQuaternion rot;
|
||||
LLVector3 position, scale;
|
||||
|
|
@ -285,16 +303,18 @@ bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionT
|
|||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
{
|
||||
std::string jointName = poserJoint_iter->jointName();
|
||||
S32 jointNumber = poserJoint_iter->getJointNumber();
|
||||
std::string jointName = poserJoint_iter->jointName();
|
||||
|
||||
bool motionIsForThisJoint = selectedJointNames.find(jointName) != std::string::npos;
|
||||
bool motionIsForThisJoint =
|
||||
std::find(selectedJointNumbers.begin(), selectedJointNumbers.end(), jointNumber) != selectedJointNumbers.end();
|
||||
if (!motionIsForAllJoints && !motionIsForThisJoint)
|
||||
continue;
|
||||
|
||||
hasRotation = hasPosition = hasScale = false;
|
||||
motionToLoadAsFsPosingMotion->getJointStateAtTime(jointName, timeToLoadAt, &hasRotation, &rot, &hasPosition, &position, &hasScale, &scale);
|
||||
|
||||
if (hasRotation)
|
||||
if (hasRotation && !poserJoint_iter->userHasSetBaseRotationToZero())
|
||||
poserJoint_iter->setBaseRotation(rot, priority);
|
||||
|
||||
if (hasPosition)
|
||||
|
|
@ -337,16 +357,17 @@ void FSPosingMotion::getJointStateAtTime(std::string jointPoseName, F32 timeToLo
|
|||
}
|
||||
}
|
||||
|
||||
bool FSPosingMotion::otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames)
|
||||
bool FSPosingMotion::otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::vector<S32> recapturedJointNumbers)
|
||||
{
|
||||
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToQuery);
|
||||
if (!motionToLoadAsFsPosingMotion)
|
||||
return false;
|
||||
|
||||
return motionToLoadAsFsPosingMotion->motionAnimatesJoints(recapturedJointNames);
|
||||
return motionToLoadAsFsPosingMotion->motionAnimatesJoints(recapturedJointNumbers);
|
||||
}
|
||||
|
||||
bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
|
||||
// Do not try to access FSPosingMotion state; you are a LLKeyframeMotion cast as a FSPosingMotion, NOT an FSPosingMotion.
|
||||
bool FSPosingMotion::motionAnimatesJoints(std::vector<int> recapturedJointNumbers)
|
||||
{
|
||||
if (mJointMotionList == nullptr)
|
||||
return false;
|
||||
|
|
@ -354,7 +375,9 @@ bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
|
|||
for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++)
|
||||
{
|
||||
JointMotion* jm = mJointMotionList->getJointMotion(i);
|
||||
if (recapturedJointNames.find(jm->mJointName) == std::string::npos)
|
||||
LLJoint* joint = mCharacter->getJoint(jm->mJointName);
|
||||
|
||||
if (std::find(recapturedJointNumbers.begin(), recapturedJointNumbers.end(), joint->getJointNum()) == recapturedJointNumbers.end())
|
||||
continue;
|
||||
|
||||
if (jm->mRotationCurve.mNumKeys > 0)
|
||||
|
|
@ -364,16 +387,15 @@ bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
|
|||
return false;
|
||||
}
|
||||
|
||||
void FSPosingMotion::resetBonePriority(std::string boneNamesToReset)
|
||||
void FSPosingMotion::resetBonePriority(std::vector<S32> boneNumbersToReset)
|
||||
{
|
||||
if (boneNamesToReset.empty())
|
||||
return;
|
||||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
for (S32 boneNumber : boneNumbersToReset)
|
||||
{
|
||||
std::string jointName = poserJoint_iter->jointName();
|
||||
if (boneNamesToReset.find(jointName) != std::string::npos)
|
||||
poserJoint_iter->setJointPriority(LLJoint::LOW_PRIORITY);
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
{
|
||||
if (poserJoint_iter->getJointNumber() == boneNumber)
|
||||
poserJoint_iter->setJointPriority(LLJoint::LOW_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,13 @@ public:
|
|||
/// <returns>The matching joint pose, if found, otherwise null.</returns>
|
||||
FSJointPose* getJointPoseByJointName(const std::string& name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the joint pose by its LLJoint number.
|
||||
/// </summary>
|
||||
/// <param name="number">The number of the joint to get the pose for.</param>
|
||||
/// <returns>The matching joint pose, if found, otherwise null.</returns>
|
||||
FSJointPose* getJointPoseByJointNumber(const S32& number);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the motion identity for this animation.
|
||||
/// </summary>
|
||||
|
|
@ -139,9 +146,9 @@ public:
|
|||
/// </summary>
|
||||
/// <param name="motionToLoad">The motion whose joint rotations (etc) we want to copy to this.</param>
|
||||
/// <param name="timeToLoadAt">The play-time the animation should be advanced to derive the correct joint state.</param>
|
||||
/// <param name="selectedJointNames">If only some of the joints should be animated by this motion, name them here.</param>
|
||||
/// <param name="selectedJointNumbers">If only some of the joints should be animated by this motion, number them here.</param>
|
||||
/// <returns></returns>
|
||||
bool loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames);
|
||||
bool loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::vector<S32> selectedJointNumbers);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the rotation, position and scale for the supplied joint name at the supplied time.
|
||||
|
|
@ -164,27 +171,27 @@ public:
|
|||
/// <summary>
|
||||
/// Resets the bone priority to zero for the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="boneNamesToReset">The string containg bone names (like mPelvis).</param>
|
||||
void resetBonePriority(std::string boneNamesToReset);
|
||||
/// <param name="boneNumbersToReset">The vector containing bone numbers.</param>
|
||||
void resetBonePriority(std::vector<S32> boneNumbersToReset);
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the supplied motion animates any of the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="motionToQuery">The motion to query.</param>
|
||||
/// <param name="recapturedJointNames">A string containing all of the joint names.</param>
|
||||
/// <param name="recapturedJointNumbers">A string containing all of the joint numbers.</param>
|
||||
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
|
||||
bool otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames);
|
||||
bool otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::vector<S32> recapturedJointNumbers);
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the this motion animates any of the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="recapturedJointNames">A string containing all of the joint names.</param>
|
||||
/// <param name="recapturedJointNames">A vector containing all of the joint numbers this motion animates.</param>
|
||||
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// The most significant thing this method does is provide access to protected properties of an LLPosingMotion.
|
||||
/// Thus its most common usage would be to access those properties for an arbitrary animation.
|
||||
/// </remarks>
|
||||
bool motionAnimatesJoints(std::string recapturedJointNames);
|
||||
bool motionAnimatesJoints(std::vector<S32> recapturedJointNumbers);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -272,7 +272,8 @@ public:
|
|||
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 ); };
|
||||
void setJoint(LLJoint* joint) { mManip->setJoint(joint); };
|
||||
void setReferenceFrame(E_PoserReferenceFrame frame) { mManip->setReferenceFrame(frame); };
|
||||
|
||||
// Optional override if you have SHIFT/CTRL combos
|
||||
virtual LLTool* getOverrideTool(MASK mask) override;
|
||||
|
|
|
|||
|
|
@ -83,8 +83,6 @@ width="430">
|
|||
<string name="joint_transform_mFaceCheekLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mFaceCheekUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mFaceCheekLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mFaceLipUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mFaceLipUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mHandThumb1Left" translate="false">SWAP_ROLL_AND_PITCH</string>
|
||||
<string name="joint_transform_mHandThumb1Right" translate="false">SWAP_ROLL_AND_PITCH NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mHandThumb2Left" translate="false">SWAP_ROLL_AND_PITCH</string>
|
||||
|
|
@ -160,6 +158,105 @@ width="430">
|
|||
<string name="joint_transform_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_transform_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
|
||||
|
||||
<!-- Begins with joint_transform_ then has an internal joint name, ONE swap choice of:
|
||||
SWAP_NOTHING, SWAP_YAW_AND_ROLL, SWAP_YAW_AND_PITCH , SWAP_ROLL_AND_PITCH, SWAP_X2Y_Y2Z_Z2X, SWAP_X2Z_Y2X_Z2Y -->
|
||||
<string name="joint_frame_mPelvis" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mTorso" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mChest" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mNeck" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHead" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mCollarLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mShoulderLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mElbowLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mWristLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mCollarRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mShoulderRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mElbowRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mWristRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mHipLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mKneeLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mAnkleLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mFootLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mToeLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mHipRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mKneeRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mAnkleRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mFootRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mToeRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
|
||||
<string name="joint_frame_mHandThumb1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandThumb1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandThumb2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandThumb2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandThumb3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandThumb3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandIndex3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandMiddle3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandRing3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mHandPinky3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mEyeRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mEyeLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceTeethLower" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceTeethUpper" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipCornerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipCornerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipUpperCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipLowerCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceLipLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyeLidUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyeLidUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyecornerInnerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyecornerInnerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyeLidLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyeLidLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowOuterLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowOuterRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowCenterLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowCenterRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowInnerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEyebrowInnerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceCheekUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceCheekLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceCheekUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceCheekLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceTongueBase" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceTongueTip" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEar1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEar2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEar1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceEar2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceNoseBase" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceNoseBridge" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceNoseLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceNoseCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceNoseRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceForeheadLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
<string name="joint_frame_mFaceJaw" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
|
||||
|
||||
<!-- For the joints/bones/thingos that are apt to Gimbal Lock, we may instead apply rotations as deltas by default, which never lock -->
|
||||
<string name="joint_delta_rotate_mHipLeft">true</string>
|
||||
<string name="joint_delta_rotate_mHipRight">true</string>
|
||||
|
|
@ -416,7 +513,7 @@ width="430">
|
|||
layout="topleft"
|
||||
mouse_opaque="false"
|
||||
left="5"
|
||||
name="title"
|
||||
name="move_tab_panel"
|
||||
top="0"
|
||||
width="235">
|
||||
<text follows="left|top"
|
||||
|
|
@ -873,7 +970,7 @@ width="430">
|
|||
name="refresh_avatars"
|
||||
tool_tip="Refresh the list of avatars and animeshes"
|
||||
width="20"
|
||||
top="232"
|
||||
top="221"
|
||||
left="3">
|
||||
<button.commit_callback
|
||||
function="Poser.RefreshAvatars"/>
|
||||
|
|
@ -894,7 +991,7 @@ width="430">
|
|||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
tool_tip="Start posing the selected avatar or animesh, if you are allowed to"
|
||||
tool_tip="Start posing the selected avatar or animesh, if you are allowed to."
|
||||
name="start_stop_posing_button"
|
||||
width="150">
|
||||
<button.commit_callback
|
||||
|
|
@ -1018,7 +1115,7 @@ width="430">
|
|||
label="Show joint markers"
|
||||
follows="left|top"
|
||||
left="5"
|
||||
tool_tip="Show small indicators to aid joint selection when visually posing."
|
||||
tool_tip="Show small indicators to aid joint selection when posing."
|
||||
top_pad="5"
|
||||
width="134" />
|
||||
<check_box
|
||||
|
|
@ -1627,30 +1724,108 @@ width="430">
|
|||
<button.commit_callback
|
||||
function="Poser.ToggleSympatheticChanges"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="18"
|
||||
is_toggle="true"
|
||||
layout="topleft"
|
||||
label="W"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
button_flash_enable="true"
|
||||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
name="poser_world_frame_toggle"
|
||||
left_pad="1"
|
||||
left="2"
|
||||
top_pad="0"
|
||||
tool_tip="Make rotational changes relative to World"
|
||||
width="18" >
|
||||
<button.commit_callback
|
||||
function="Poser.ToggleRotationFrame"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="18"
|
||||
is_toggle="true"
|
||||
layout="topleft"
|
||||
label="A"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
button_flash_enable="true"
|
||||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
name="poser_avatar_frame_toggle"
|
||||
left_pad="0"
|
||||
top_delta="0"
|
||||
tool_tip="Make rotational changes relative to Avatar"
|
||||
width="15" >
|
||||
<button.commit_callback
|
||||
function="Poser.ToggleRotationFrame"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="18"
|
||||
is_toggle="true"
|
||||
layout="topleft"
|
||||
label="S"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
button_flash_enable="true"
|
||||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
name="poser_screen_frame_toggle"
|
||||
left_pad="0"
|
||||
top_delta="0"
|
||||
tool_tip="Make rotational changes relative to Screen"
|
||||
width="15" >
|
||||
<button.commit_callback
|
||||
function="Poser.ToggleRotationFrame"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="18"
|
||||
layout="topleft"
|
||||
label="Copy L > R"
|
||||
name="button_symmetrize_left_to_right"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
button_flash_enable="true"
|
||||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
tool_tip="Click to copy change from left side to right side."
|
||||
left="14"
|
||||
top_pad="0"
|
||||
width="70" >
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="67" >
|
||||
<button.commit_callback
|
||||
function="Poser.Symmetrize"
|
||||
parameter="1"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="19"
|
||||
height="18"
|
||||
layout="topleft"
|
||||
label="Copy R > L"
|
||||
name="button_symmetrize_right_to_left"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
button_flash_enable="true"
|
||||
flash_color="0.7 0.7 1 1"
|
||||
button_flash_count="64"
|
||||
button_flash_rate="0.5"
|
||||
tool_tip="Click to copy change from right side to left side."
|
||||
left_pad="23"
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="70" >
|
||||
width="67" >
|
||||
<button.commit_callback
|
||||
function="Poser.Symmetrize"
|
||||
parameter="2"/>
|
||||
|
|
@ -1795,7 +1970,7 @@ width="430">
|
|||
left_pad="0"
|
||||
top_delta="0"
|
||||
name="button_spacer_panel"
|
||||
width="80"/>
|
||||
width="82"/>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="21"
|
||||
|
|
|
|||
Loading…
Reference in New Issue