Poser: Add Manip translate

meow-7.2.2
Angeldark Raymaker 2025-12-31 11:09:18 +00:00
parent d4f3a52256
commit 854e5c6916
14 changed files with 2066 additions and 87 deletions

View File

@ -150,6 +150,7 @@ set(viewer_SOURCE_FILES
fslslpreproc.cpp
fslslpreprocviewer.cpp
fsmaniprotatejoint.cpp
fsmaniptranslatejoint.cpp
fsmoneytracker.cpp
fsnamelistavatarmenu.cpp
fsnearbychatbarlistener.cpp
@ -997,6 +998,7 @@ set(viewer_HEADER_FILES
fslslpreprocviewer.h
fsmoneytracker.h
fsmaniprotatejoint.h
fsmaniptranslatejoint.h
fsnamelistavatarmenu.h
fsnearbychatbarlistener.h
fsnearbychatcontrol.h

View File

@ -276,10 +276,8 @@ void FSFloaterPoser::onFocusReceived()
void FSFloaterPoser::onFocusLost()
{
if( LLEditMenuHandler::gEditMenuHandler == this )
{
if (LLEditMenuHandler::gEditMenuHandler == this)
LLEditMenuHandler::gEditMenuHandler = nullptr;
}
}
void FSFloaterPoser::draw()
@ -361,22 +359,33 @@ void FSFloaterPoser::enableVisualManipulators()
}
if (LLToolMgr::getInstance()->getCurrentToolset() != gCameraToolset)
{
mLastToolset = LLToolMgr::getInstance()->getCurrentToolset();
}
LLToolMgr::getInstance()->setCurrentToolset(gPoserToolset);
LLToolMgr::getInstance()->getCurrentToolset()->selectTool(FSToolCompPose::getInstance());
FSToolCompPose::getInstance()->setAvatar(gAgentAvatarp);
LLVOAvatar* avatar = getUiSelectedAvatar();
setVisualManipulators(avatar);
}
void FSFloaterPoser::disableVisualManipulators()
{
if (mLastToolset)
{
LLToolMgr::getInstance()->setCurrentToolset(mLastToolset);
}
FSToolCompPose::getInstance()->setAvatar(nullptr);
setVisualManipulators(nullptr);
}
void FSFloaterPoser::setVisualManipulators(LLVOAvatar* avatar)
{
FSToolCompPose::getInstance()->setAvatar(avatar);
FSToolCompPoseTranslate::getInstance()->setAvatar(avatar);
if (!avatar)
return;
auto selectedJoints = getUiSelectedPoserJoints();
updateManipWithFirstSelectedJoint(selectedJoints);
}
void FSFloaterPoser::onClose(bool app_quitting)
@ -1657,6 +1666,7 @@ void FSFloaterPoser::onToggleRotationFrameButton(const LLUICtrl* toggleButton)
}
FSToolCompPose::getInstance()->setReferenceFrame(getReferenceFrame());
FSToolCompPoseTranslate::getInstance()->setReferenceFrame(getReferenceFrame());
refreshRotationSlidersAndSpinners();
}
@ -1949,9 +1959,15 @@ void FSFloaterPoser::updateManipWithFirstSelectedJoint(const std::vector<FSPoser
bool iCanPoseThem = havePermissionToAnimateOtherAvatar(avatarp);
if ((joints.size() >= 1) && (haveImplicitPermission || iCanPoseThem))
{
FSToolCompPose::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName()));
FSToolCompPoseTranslate::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName()));
}
else
{
FSToolCompPose::getInstance()->setJoint(nullptr);
FSToolCompPoseTranslate::getInstance()->setJoint(nullptr);
}
}
E_RotationStyle FSFloaterPoser::getUiSelectedBoneRotationStyle(const std::string& jointName) const
@ -2476,9 +2492,15 @@ void FSFloaterPoser::onAvatarSelect()
bool haveExplicitPermission = havePermissionToAnimateOtherAvatar(avatar); // as permissions allow
if (haveImplicitPermission || haveExplicitPermission)
{
FSToolCompPose::getInstance()->setAvatar(avatar);
FSToolCompPoseTranslate::getInstance()->setAvatar(avatar);
}
else
{
FSToolCompPose::getInstance()->setAvatar(nullptr);
FSToolCompPoseTranslate::getInstance()->setAvatar(nullptr);
}
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);

View File

@ -252,6 +252,7 @@ public:
// visual manipulators control
void enableVisualManipulators();
void disableVisualManipulators();
void setVisualManipulators(LLVOAvatar* avatar);
// Visual cue for which bone is under the mouse-cursor
void drawOnHoverJointHint();

View File

@ -55,47 +55,49 @@ void FSJointPose::setPublicPosition(const LLVector3& pos)
{
addStateToUndo(mCurrentState);
mCurrentState.mPosition.set(pos);
mCurrentState.mLastChangeWasRotational = false;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
}
void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
void FSJointPose::setPublicRotation(bool zeroBase, E_PoserChangeType changeType, const LLQuaternion& rot)
{
addStateToUndo(mCurrentState);
if (zeroBase)
zeroBaseRotation(true);
else
mCurrentState.mUserSpecifiedBaseZero = false;
mCurrentState.mUserSpecifiedBaseZero = POSER_CHANGE_DEFAULT;
mCurrentState.mRotation.set(rot);
mCurrentState.mLastChangeWasRotational = true;
mCurrentState.mPoserChangeType = changeType;
}
void FSJointPose::setPublicScale(const LLVector3& scale)
{
addStateToUndo(mCurrentState);
mCurrentState.mScale.set(scale);
mCurrentState.mLastChangeWasRotational = false;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
}
bool FSJointPose::undoLastChange()
E_PoserChangeType FSJointPose::undoLastChange()
{
bool changeType = mCurrentState.mLastChangeWasRotational;
E_PoserChangeType changeType = mCurrentState.mPoserChangeType;
mCurrentState = undoLastStateChange(mCurrentState);
return changeType;
}
void FSJointPose::redoLastChange()
E_PoserChangeType FSJointPose::redoLastChange()
{
mCurrentState = redoLastStateChange(mCurrentState);
return mCurrentState.mPoserChangeType;
}
void FSJointPose::resetJoint()
{
addStateToUndo(mCurrentState);
mCurrentState.resetJoint();
mCurrentState.mLastChangeWasRotational = true;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
}
void FSJointPose::addStateToUndo(const FSJointState& stateToAddToUndo)
@ -169,13 +171,13 @@ void FSJointPose::recaptureJoint()
}
mCurrentState = FSJointState(joint);
mCurrentState.mLastChangeWasRotational = true;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
}
LLQuaternion FSJointPose::updateJointAsDelta(bool zeroBase, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale)
{
addStateToUndo(mCurrentState);
mCurrentState.mLastChangeWasRotational = true;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
return mCurrentState.updateFromJointProperties(zeroBase, rotation, position, scale);
}
@ -231,7 +233,7 @@ void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint)
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.cloneRotationFrom(fromJoint->mCurrentState);
mCurrentState.mLastChangeWasRotational = true;
mCurrentState.mPoserChangeType = POSER_CHANGE_DEFAULT;
}
void FSJointPose::mirrorRotationFrom(FSJointPose* fromJoint)

View File

@ -32,6 +32,17 @@
//-----------------------------------------------------------------------------
#include "llmotion.h"
/// <summary>
/// Describes the kind of pose-change.
/// Useful for undoing complex changes.
/// </summary>
typedef enum E_PoserChangeType
{
POSER_CHANGE_DEFAULT = 0, // a simple change to pos/rot/scale
POSER_CHANGE_CHILDMOVE = 1, // position change of a child by rotating its 2 antecedent joints
POSER_CHANGE_ROTATION = 2, // a rotation change
} E_PoserChangeType;
//-----------------------------------------------------------------------------
// class FSJointPose
//-----------------------------------------------------------------------------
@ -70,13 +81,14 @@ class FSJointPose
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
/// <returns>true if the change we un-did was rotational.</returns>
bool undoLastChange();
/// <returns>A value indicating what kind of change was undone (important for undo).</returns>
E_PoserChangeType undoLastChange();
/// <summary>
/// Undoes the last position set, if any.
/// </summary>
void redoLastChange();
/// <returns>A value indicating what kind of change was redone.</returns>
E_PoserChangeType redoLastChange();
/// <summary>
/// Resets the joint to its conditions when posing started.
@ -100,7 +112,7 @@ class FSJointPose
/// 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);
void setPublicRotation(bool zeroBase, E_PoserChangeType changeType, const LLQuaternion& rot);
/// <summary>
/// Reflects the base and delta rotation of the represented joint left-right.
@ -319,7 +331,7 @@ class FSJointPose
mUserSpecifiedBaseZero = false;
mRotationIsWorldLocked = false;
mJointRotationIsMirrored = false;
mLastChangeWasRotational = true;
mPoserChangeType = POSER_CHANGE_DEFAULT;
mBaseRotation.set(mStartingRotation);
mRotation.set(LLQuaternion::DEFAULT);
mPosition.setZero();
@ -417,15 +429,15 @@ class FSJointPose
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
mBasePriority = state->mBasePriority;
mJointRotationIsMirrored = state->mJointRotationIsMirrored;
mLastChangeWasRotational = state->mLastChangeWasRotational;
mPoserChangeType = state->mPoserChangeType;
}
public:
LLQuaternion mRotation;
LLVector3 mPosition;
LLVector3 mScale;
bool mRotationIsWorldLocked = false;
bool mLastChangeWasRotational = false;
LLQuaternion mRotation;
LLVector3 mPosition;
LLVector3 mScale;
bool mRotationIsWorldLocked = false;
E_PoserChangeType mPoserChangeType = POSER_CHANGE_DEFAULT;
/// <summary>
/// Whether the joint has been mirrored.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
/**
* @file fsmaniptranslatejoint.h
* @brief custom manipulator for moving joints
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (c) 2025 Angeldark Raymaker @ Second Life
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FS_MANIP_TRANSLATE_JOINT_H
#define FS_MANIP_TRANSLATE_JOINT_H
#include "llselectmgr.h"
#include "fsmaniprotatejoint.h"
#include "llmaniptranslate.h"
class LLJoint;
class LLVOAvatar; // for LLVOAvatarSelf, etc.
class FSManipTranslateJoint : public LLManipTranslate
{
public:
class ManipulatorHandle
{
public:
LLVector3 mStartPosition;
LLVector3 mEndPosition;
EManipPart mManipID;
F32 mHotSpotRadius;
ManipulatorHandle(LLVector3 start_pos, LLVector3 end_pos, EManipPart id, F32 radius):mStartPosition(start_pos), mEndPosition(end_pos), mManipID(id), mHotSpotRadius(radius){}
};
FSManipTranslateJoint(LLToolComposite* composite);
virtual ~FSManipTranslateJoint() {};
/// <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 avatar the manip should interact with.
/// </summary>
/// <param name="avatar">The avatar to interact with.</param>
void setReferenceFrame(const E_PoserReferenceFrame frame) { mReferenceFrame = frame; };
static U32 getGridTexName() ;
static void destroyGL();
static void restoreGL();
virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
virtual bool handleHover(S32 x, S32 y, MASK mask);
virtual void render();
virtual void handleSelect();
virtual void highlightManipulators(S32 x, S32 y);
virtual bool handleMouseDownOnPart(S32 x, S32 y, MASK mask);
virtual bool canAffectSelection();
protected:
enum EHandleType {
HANDLE_CONE,
HANDLE_BOX,
HANDLE_SPHERE
};
void renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, bool reverse_direction);
void renderTranslationHandles();
void renderText();
private:
bool isAvatarJointSafeToUse();
/// <summary>
/// Determines if the currently selected joint is one to move with manip.
/// </summary>
/// <returns>True if we can move the joint with manip, otherwise false.</returns>
bool isMoveableJoint();
void getManipJointNormal(LLJoint* joint, EManipPart manip, LLVector3& normal) { getManipNormal(nullptr, manip, normal);};
bool getManipJointAxis(LLJoint* joint, EManipPart manip, LLVector3& axis) { return getManipAxis(nullptr, manip, axis); };
LLVector3 getChangeInPosition(LLVector3 newPosition) const;
S32 mLastHoverMouseX;
S32 mLastHoverMouseY;
bool mMouseOutsideSlop; // true after mouse goes outside slop region
bool mCopyMadeThisDrag;
S32 mMouseDownX;
S32 mMouseDownY;
F32 mAxisArrowLength; // pixels
F32 mConeSize; // meters, world space
F32 mArrowLengthMeters; // meters
F32 mPlaneManipOffsetMeters;
LLVector3 mManipNormal;
LLVector3 mDragCursorLastGlobal;
LLVector3d mDragCursorStartGlobal;
LLVector3d mDragSelectionStartGlobal;
LLTimer mUpdateTimer;
LLVector4 mManipulatorVertices[18];
F32 mSnapOffsetMeters;
LLVector3 mSnapOffsetAxis;
LLQuaternion mGridRotation;
LLVector3 mGridOrigin;
LLVector3 mGridScale;
LLVector3 mArrowScales;
LLVector3 mPlaneScales;
LLVector4 mPlaneManipPositions;
LLJoint* mJoint = nullptr;
LLVOAvatar* mAvatar = nullptr;
E_PoserReferenceFrame mReferenceFrame = POSER_FRAME_BONE;
};
#endif

View File

@ -114,9 +114,26 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
if (!jointPose)
return;
if (jointPose->undoLastChange())
E_PoserChangeType changeType = jointPose->undoLastChange();
if (changeType == POSER_CHANGE_ROTATION)
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
if (changeType == POSER_CHANGE_CHILDMOVE)
{
undoOrRedoRotatedParents(joint, posingMotion, false);
FSJointPose* parentJointPose = findParentJointPose(posingMotion, joint.jointName());
if (parentJointPose)
{
FSJointPose* grandParentJointPose = findParentJointPose(posingMotion, parentJointPose->jointName());
if (grandParentJointPose)
{
auto grandParentPoserJoint = getPoserJointByName(grandParentJointPose->jointName());
undoOrRedoWorldLockedDescendants(*grandParentPoserJoint, posingMotion, false);
}
}
}
if (style == NONE || style == DELTAMODE)
return;
@ -124,7 +141,8 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
if (!oppositeJointPose)
return;
if (!oppositeJointPose->undoLastChange())
changeType = oppositeJointPose->undoLastChange();
if (changeType != POSER_CHANGE_ROTATION)
return;
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
@ -198,8 +216,10 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
if (!jointPose)
return;
jointPose->redoLastChange();
E_PoserChangeType changeType = jointPose->redoLastChange();
undoOrRedoWorldLockedDescendants(joint, posingMotion, true);
if (changeType == POSER_CHANGE_CHILDMOVE)
undoOrRedoRotatedParents(joint, posingMotion, true);
if (style == NONE || style == DELTAMODE)
return;
@ -465,6 +485,15 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
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 (!position.isExactlyZero())
updateJointPositionFromManip(avatar, joint, resetBaseRotationToZero, style, position);
else
updateJointRotationFromManip(avatar, joint, resetBaseRotationToZero, style, frame, rotation);
}
void FSPoserAnimator::updateJointRotationFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, const LLQuaternion& rotation)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -478,7 +507,7 @@ void FSPoserAnimator::updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoin
return;
LLQuaternion framedRotation = changeToRotationFrame(avatar, rotation, frame, jointPose);
jointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION, framedRotation * jointPose->getPublicRotation());
deRotateWorldLockedDescendants(joint, posingMotion, framedRotation);
@ -495,14 +524,16 @@ void FSPoserAnimator::updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoin
{
case SYMPATHETIC:
case SYMPATHETIC_DELTA:
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION,
framedRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, framedRotation);
break;
case MIRROR:
case MIRROR_DELTA:
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION,
mirroredRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
break;
@ -512,6 +543,207 @@ void FSPoserAnimator::updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoin
}
}
void FSPoserAnimator::updateJointPositionFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style, const LLVector3& position)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
LLJoint* movedJoint = avatar->getJoint(joint->jointName());
if (!movedJoint)
return;
LLJoint* parentJoint = movedJoint->getParent();
if (!parentJoint)
return;
if (jointIsPelvis(movedJoint))
{
auto movedPoserJoint = getPoserJointByName(joint->jointName());
LLVector3 currentPosition = getJointPosition(avatar, *movedPoserJoint);
LLQuaternion rootWorldRot = parentJoint->getWorldRotation();
currentPosition += position * ~rootWorldRot;
setJointPosition(avatar, movedPoserJoint, currentPosition, POSER_FRAME_WORLD, NONE);
return;
}
if (jointIsPelvis(parentJoint))
return;
LLJoint* grandParentJoint = parentJoint->getParent();
if (!grandParentJoint || jointIsPelvis(grandParentJoint))
return;
LLVector3 oldMovedJointPosition = movedJoint->getWorldPosition();
LLVector3 parentPosition = parentJoint->getWorldPosition();
LLVector3 grandParentPosition = grandParentJoint->getWorldPosition();
LLVector3 newMovedJointPosition = oldMovedJointPosition + position;
LLQuaternion grandParentWorldRot = grandParentJoint->getWorldRotation();
LLQuaternion parentWorldRot = parentJoint->getWorldRotation();
FSJointPose* grandParentJointPose = posingMotion->getJointPoseByJointName(grandParentJoint->getName());
FSJointPose* parentJointPose = posingMotion->getJointPoseByJointName(parentJoint->getName());
FSJointPose* movedJointPose = posingMotion->getJointPoseByJointName(movedJoint->getName());
if (!grandParentJointPose || !parentJointPose)
return;
LLQuaternion grandParentFrameRotChange, grandParentLengthRotChange, parentLengthRotChange;
if (parentJointIsHinge(parentJoint))
{
if (!canMoveToNewPosition(grandParentPosition, parentPosition, oldMovedJointPosition, newMovedJointPosition))
return;
grandParentFrameRotChange = getAbcFrameRotation(grandParentPosition, parentPosition, oldMovedJointPosition, position);
if (!getPlanarRotationAtAandB(grandParentPosition, parentPosition, oldMovedJointPosition, position, grandParentLengthRotChange,
parentLengthRotChange))
return;
}
else
{
grandParentFrameRotChange.loadIdentity(); // unused here
if (!getFreeRotationAtAandB(grandParentPosition, parentPosition, oldMovedJointPosition, position, grandParentLengthRotChange,
parentLengthRotChange))
return;
}
// convert rotations to local frame
grandParentFrameRotChange = grandParentWorldRot * grandParentFrameRotChange * ~grandParentWorldRot;
grandParentLengthRotChange = grandParentWorldRot * grandParentLengthRotChange * ~grandParentWorldRot;
parentLengthRotChange = parentWorldRot * parentLengthRotChange * ~parentWorldRot;
movedJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_CHILDMOVE, movedJointPose->getPublicRotation()); // set up undo
parentJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION,
parentLengthRotChange * parentJointPose->getPublicRotation());
grandParentJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION,
grandParentLengthRotChange * grandParentJointPose->getPublicRotation());
grandParentJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_ROTATION,
grandParentFrameRotChange * grandParentJointPose->getPublicRotation());
auto grandParentPoserJoint = getPoserJointByName(grandParentJoint->getName());
deRotateWorldLockedDescendants(grandParentPoserJoint, posingMotion, grandParentLengthRotChange);
}
bool FSPoserAnimator::getFreeRotationAtAandB(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC,
LLQuaternion& changeAtA, LLQuaternion& changeAtB)
{
LLVector3 vectorAC = posC - posA;
LLVector3 vectorAB = posB - posA;
F32 lengthAC = vectorAC.length();
F32 lengthAB = vectorAB.length();
F32 lengthBC = (posC - posB).length();
F32 lengthMove = changeAtC.length();
LLVector3 rotationAxis = vectorAC % changeAtC;
rotationAxis.normalize();
F32 angle = asin(lengthMove / lengthAC);
if (isnan(angle))
return false;
changeAtA = LLQuaternion(angle, rotationAxis);
changeAtB = LLQuaternion(-angle, rotationAxis);
return true;
}
bool FSPoserAnimator::getPlanarRotationAtAandB(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC,
LLQuaternion& changeAtA, LLQuaternion& changeAtB)
{
LLVector3 vectorAC = posC - posA;
LLVector3 vectorAB = posB - posA;
F32 lengthAC = vectorAC.length();
F32 lengthAB = vectorAB.length();
F32 lengthBC = (posC - posB).length();
F32 lengthACbeforeRot = vectorAC.length();
F32 lengthACafterRot = (posC + changeAtC - posA).length();
LLVector3 rotationAxis = vectorAB % vectorAC;
rotationAxis.normalize();
// Distance from posA, along AC, to where B is perpendicular to AC (right angle triangle), before and after moving C; intersecting spheres
F32 dist1 = (lengthAB * lengthAB - lengthBC * lengthBC + lengthACbeforeRot * lengthACbeforeRot) / (2.f * lengthACbeforeRot);
F32 dist2 = (lengthAB * lengthAB - lengthBC * lengthBC + lengthACafterRot * lengthACafterRot) / (2.f * lengthACafterRot);
if (dist1 == dist2)
return false;
F32 angle1 = acos(dist1 / lengthAB);
F32 angle2 = acos(dist2 / lengthAB);
changeAtA = LLQuaternion((angle1 - angle2), rotationAxis);
angle1 = acos((lengthACbeforeRot - dist1) / lengthBC);
angle2 = acos((lengthACafterRot - dist2) / lengthBC);
changeAtB = LLQuaternion((angle2 - angle1), rotationAxis);
return true;
}
LLQuaternion FSPoserAnimator::getAbcFrameRotation(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC)
{
LLVector3 vectorAC = posC - posA;
LLVector3 vectorAnewC = vectorAC + changeAtC;
F32 angle = acos((vectorAC * vectorAnewC) / (vectorAC.length() * vectorAnewC.length()));
LLVector3 rotationAxis = vectorAC % changeAtC;
rotationAxis.normalize();
if (isnan(angle))
angle = 0.f;
return LLQuaternion(angle, rotationAxis);
}
bool FSPoserAnimator::parentJointIsHinge(LLJoint* parentJoint)
{
if (!parentJoint)
return false;
std::string jointName = parentJoint->getName();
if (jointName.find("Elbow") != std::string::npos)
return true;
if (jointName.find("Knee") != std::string::npos)
return true;
if (jointName.find("Hand") != std::string::npos && jointName.find("2") != std::string::npos) // eg: mHandIndex2Right, the second 'knuckle'
return true;
return false;
}
bool FSPoserAnimator::jointIsPelvis(const LLJoint* joint)
{
if (!joint)
return false;
std::string jointName = joint->getName();
return jointName == "mPelvis" || jointName == "mRoot";
}
bool FSPoserAnimator::canMoveToNewPosition(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 posNewC)
{
F32 lengthAC = (posNewC - posA).length();
if (lengthAC < 0.04f)
return false; // don't allow C and A to converge
F32 lengthAB = (posB - posA).length();
F32 lengthBC = (posC - posB).length();
if (lengthAC > lengthAB + lengthBC - 0.002f)
return false; // if the limbs become fully extended then moving back is non-trivial because the 3 joints are not a triangle
if (lengthAC < fabs(lengthAB - lengthBC))
return false; // absurdity
return true;
}
LLQuaternion FSPoserAnimator::getManipGimbalRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, E_PoserReferenceFrame frame)
{
LLQuaternion globalRot(-1.f, 0.f, 0.f, 0.f);
@ -548,16 +780,19 @@ LLQuaternion FSPoserAnimator::getManipGimbalRotation(LLVOAvatar* avatar, const F
if (frame == POSER_FRAME_BONE)
return llJoint->getWorldRotation();
return getQuaternionFromWorldVector(llJoint->getWorldPosition() - gAgentCamera.getCameraPositionAgent());
}
LLQuaternion FSPoserAnimator::getQuaternionFromWorldVector(const LLVector3 worldVector)
{
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);
LLVector3 up, normVector(worldVector);
normVector.normalize();
left.setVec(skyward % normVector);
up.setVec(normVector % left);
return LLQuaternion(jointToCameraPosition, left, up);
return LLQuaternion(normVector, left, up);
}
LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const
@ -633,28 +868,28 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case SYMPATHETIC:
case MIRROR:
if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, deltaRot * jointPose->getPublicRotation());
else
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, absRot);
break;
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, deltaRot * jointPose->getPublicRotation());
break;
case DELTAMODE:
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, deltaRot * jointPose->getPublicRotation());
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
case NONE:
default:
if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, deltaRot * jointPose->getPublicRotation());
else
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
jointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT, absRot);
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
@ -677,7 +912,8 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
break;
case SYMPATHETIC_DELTA:
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT,
deltaRot * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
break;
@ -689,7 +925,8 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
break;
case MIRROR_DELTA:
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, POSER_CHANGE_DEFAULT,
mirroredRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
break;
@ -1071,7 +1308,7 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
jointPose->purgeUndoQueue();
LLQuaternion rot = translateRotationToQuaternion(avatar, jointPose, POSER_FRAME_BONE, SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setPublicRotation(setBaseToZero, rot);
jointPose->setPublicRotation(setBaseToZero, POSER_CHANGE_DEFAULT, rot);
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
@ -1332,10 +1569,10 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const
int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, S32 depth) const
{
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
if (joint->bvhChildren().empty())
return depth;
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
depth++;
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
@ -1352,10 +1589,10 @@ int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, S32 depth) co
void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotationChange)
{
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
if (joint->bvhChildren().empty())
return;
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(joint->jointName());
if (!parentJoint)
return;
@ -1385,14 +1622,14 @@ void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint,
LLQuaternion rotDiffInChildFrame = differenceInWorldRot * rotationChange * ~differenceInWorldRot;
rotDiffInChildFrame.conjugate();
jointPose->setPublicRotation(false, rotDiffInChildFrame * jointPose->getPublicRotation());
jointPose->setPublicRotation(false, POSER_CHANGE_ROTATION, rotDiffInChildFrame * jointPose->getPublicRotation());
return;
}
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
if (joint->bvhChildren().empty())
return;
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint->bvhChildren().at(index));
@ -1403,12 +1640,54 @@ void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint,
}
}
void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
void FSPoserAnimator::undoOrRedoRotatedParents(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
{
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
if (numberOfBvhChildNodes < 1)
if (!posingMotion)
return;
FSJointPose* parentJointPose = findParentJointPose(posingMotion, joint.jointName());
if (!parentJointPose)
return;
if (redo)
parentJointPose->redoLastChange();
else
parentJointPose->undoLastChange();
FSJointPose* grandParentJointPose = findParentJointPose(posingMotion, parentJointPose->jointName());
if (!grandParentJointPose)
return;
if (redo)
grandParentJointPose->redoLastChange();
else
grandParentJointPose->undoLastChange();
}
FSJointPose* FSPoserAnimator::findParentJointPose(FSPosingMotion* posingMotion, std::string jointName)
{
if (jointName.empty() || !posingMotion)
return nullptr;
for (size_t index = 0; index != PoserJoints.size(); ++index)
{
auto children = PoserJoints[index].bvhChildren();
auto it = std::find(children.begin(), children.end(), jointName);
if (it == children.end())
continue;
return posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
}
return nullptr;
}
void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
{
if (joint.bvhChildren().empty())
return;
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint.bvhChildren().at(index));
@ -1438,10 +1717,10 @@ void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& join
return;
}
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
if (numberOfBvhChildNodes < 1)
if (joint.bvhChildren().empty())
return;
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint.bvhChildren().at(index));

View File

@ -792,19 +792,19 @@ public:
void applyJointMirrorToBaseRotations(FSPosingMotion* posingMotion);
private:
/// <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 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.
@ -900,6 +900,88 @@ public:
/// </remarks>
LLQuaternion changeToRotationFrame(LLVOAvatar* avatar, const LLQuaternion& rotation, E_PoserReferenceFrame frame, FSJointPose* joint);
void updateJointRotationFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, const LLQuaternion& rotation);
void updateJointPositionFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style, const LLVector3& position);
/// <summary>
/// Undoes or re-does the parent and grandparent joint rotations from a updateJointPositionFromManip(...)
/// </summary>
/// <param name="joint">The joint whose parent and grandparent need to undo.</param>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="redo">Whether to redo the rotation.</param>
void undoOrRedoRotatedParents(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
/// <summary>
/// Finds a jointPose using the supplied name.
/// </summary>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="jointName">The name of the joint.</param>
/// <returns>The jointPose if found, otherwise nullptr.</returns>
FSJointPose* findParentJointPose(FSPosingMotion* posingMotion, std::string jointName);
/// <summary>
/// Determines the rotation of the grand-parent joint to move a system containing a 'hinge' joint.
/// </summary>
/// <param name="posA">The world position of the grand-parent joint.</param>
/// <param name="posC">The world position of the parent joint.</param>
/// <param name="posC">The world position of the moved joint.</param>
/// <param name="changeAtC">The world-referenced movement of the moved joint.</param>
/// <returns>The rotation required for the system of moved-joint, parent and grandparent.</returns>
/// <remarks>
/// The first of a 2-part treatment to move a joint by changing the rotation of its two parents.
/// This method determines the rotation for the entire frame (a triangle) of joint-parent-grandparent.
/// It finds the vector of the grand-parent to child (vectorAC) before and after moment, then uses
/// dot-product to determine the angle change.
/// Cross product of the change-vector and AC determines the axis of rotation.
/// </remarks>
LLQuaternion getAbcFrameRotation(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC);
/// <summary>
/// Determines the rotation changes for the grandparent and parent joints to position the child when the parent joint is a hinge.
/// </summary>
/// <param name="posA">The world position of the grand-parent joint.</param>
/// <param name="posB">The world position of the parent joint.</param>
/// <param name="posC">The world position of the moved joint.</param>
/// <param name="changeAtC">The world-referenced movement of the moved joint.</param>
/// <param name="changeAtA">The rotation at the grandparent to extend or retract the moved joint.</param>
/// <param name="changeAtB">The rotation at the parent to extend or retract the moved joint.</param>
/// <returns>True if the rotation can be made, otherwise false.</returns>
/// <remarks>
/// The second of a 2-part treatment to move a joint by changing the rotation of its two parents.
/// This method calculates rotations at grandparent and parent so that the length AC (grandparent to child) changes.
/// Both rotations are in the plane created by grandparent-parent-child.
/// This emulates the behaviour of a 'hinge' joint, like 'flexing' an elbow or knee.
/// Child movement is along vector AC (grandparent-child), and does not account for 'world movement' differences in C because of changeAtC,
/// as that is handled by getAbcFrameRotation.
/// </remarks>
bool getPlanarRotationAtAandB(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC,
LLQuaternion& changeAtA, LLQuaternion& changeAtB);
/// <summary>
/// Determines the rotation changes for the grandparent and parent joints to position the child when all joints are 'socket' joints.
/// </summary>
/// <param name="posA">The world position of the grand-parent joint.</param>
/// <param name="posB">The world position of the parent joint.</param>
/// <param name="posC">The world position of the moved joint.</param>
/// <param name="changeAtC">The world-referenced movement of the moved joint.</param>
/// <param name="changeAtA">The rotation at the grandparent to extend or retract the moved joint.</param>
/// <param name="changeAtB">The rotation at the parent to extend or retract the moved joint.</param>
/// <returns>True if the rotation can be made, otherwise false.</returns>
/// <remarks>
/// Moves the child spherically in the specified direction by rotating the parent and grandparent.
/// </remarks>
bool getFreeRotationAtAandB(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 changeAtC,
LLQuaternion& changeAtA, LLQuaternion& changeAtB);
LLQuaternion getQuaternionFromWorldVector(const LLVector3 worldVector);
bool parentJointIsHinge(LLJoint* parentJoint);
bool jointIsPelvis(const LLJoint* joint);
bool canMoveToNewPosition(const LLVector3 posA, const LLVector3 posB, const LLVector3 posC, const LLVector3 posNewC);
/// <summary>
/// Maps the avatar's ID to the animation registered to them.
/// Thus we start/stop the same animation, and get/set the same rotations etc.

View File

@ -279,7 +279,7 @@ void FSPosingMotion::setAllRotationsToZeroAndClearUndo()
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
poserJoint_iter->purgeUndoQueue();
poserJoint_iter->setPublicRotation(true, LLQuaternion::DEFAULT);
poserJoint_iter->setPublicRotation(true, POSER_CHANGE_ROTATION, LLQuaternion::DEFAULT);
}
}

View File

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

View File

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

View File

@ -125,6 +125,7 @@ void LLToolMgr::initTools()
gBasicToolset->addTool( LLToolCompInspect::getInstance() );
gFaceEditToolset->addTool( LLToolCamera::getInstance() );
gPoserToolset->addTool( FSToolCompPose::getInstance() );
gPoserToolset->addTool( FSToolCompPoseTranslate::getInstance() );
// On startup, use "select" tool
setCurrentToolset(gBasicToolset);

View File

@ -5105,9 +5105,12 @@ void LLViewerWindow::renderSelections( bool for_gl_pick, bool pick_parcel_walls,
}
// <FS:Beq> render the poser manipulator guides
// if we have something selected those toosl should override
if ( (!for_hud) && (selection->isEmpty()) && (LLToolMgr::getInstance()->getCurrentTool() == FSToolCompPose::getInstance()) )
if (!for_hud && selection->isEmpty())
{
FSToolCompPose::getInstance()->render();
if (LLToolMgr::getInstance()->getCurrentTool() == FSToolCompPose::getInstance())
FSToolCompPose::getInstance()->render();
if (LLToolMgr::getInstance()->getCurrentTool() == FSToolCompPoseTranslate::getInstance())
FSToolCompPoseTranslate::getInstance()->render();
}
// </FS:Beq>