FIRE-35769: Add 'World lock joints' option
parent
3bd58efda2
commit
00ed27bf14
|
|
@ -192,6 +192,8 @@ bool FSFloaterPoser::postBuild()
|
|||
mFlipJointBtn = getChild<LLButton>("FlipJoint_avatar");
|
||||
mRecaptureBtn = getChild<LLButton>("button_RecaptureParts");
|
||||
mTogglePosingBonesBtn = getChild<LLButton>("toggle_PosingSelectedBones");
|
||||
mToggleLockWorldRotBtn = getChild<LLButton>("toggle_LockWorldRotation");
|
||||
mToggleLockWorldRotBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onClickLockWorldRotBtn(); });
|
||||
|
||||
mToggleMirrorRotationBtn = getChild<LLButton>("button_toggleMirrorRotation");
|
||||
mToggleSympatheticRotationBtn = getChild<LLButton>("button_toggleSympatheticRotation");
|
||||
|
|
@ -517,6 +519,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
{
|
||||
std::string bone_name = pj.jointName();
|
||||
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
|
||||
bool jointRotLocked = mPoserAnimator.getRotationIsWorldLocked(avatar, pj);
|
||||
|
||||
record[bone_name] = bone_name;
|
||||
record[bone_name]["enabled"] = posingThisJoint;
|
||||
|
|
@ -532,9 +535,10 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
continue;
|
||||
|
||||
record[bone_name]["jointBaseRotationIsZero"] = baseRotationIsZero;
|
||||
record[bone_name]["rotation"] = rotation.getValue();
|
||||
record[bone_name]["position"] = position.getValue();
|
||||
record[bone_name]["scale"] = scale.getValue();
|
||||
record[bone_name]["rotation"] = rotation.getValue();
|
||||
record[bone_name]["position"] = position.getValue();
|
||||
record[bone_name]["scale"] = scale.getValue();
|
||||
record[bone_name]["worldLocked"] = jointRotLocked;
|
||||
}
|
||||
|
||||
std::string fullSavePath =
|
||||
|
|
@ -1041,6 +1045,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
LLQuaternion quat;
|
||||
bool enabled;
|
||||
bool setJointBaseRotationToZero;
|
||||
bool worldLocked;
|
||||
S32 version = 0;
|
||||
bool startFromZeroRot = true;
|
||||
|
||||
|
|
@ -1116,6 +1121,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
vec3.setValue(control_map["scale"]);
|
||||
mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
|
||||
}
|
||||
|
||||
worldLocked = control_map.has("worldLocked") ? control_map["worldLocked"].asBoolean() : false;
|
||||
mPoserAnimator.setRotationIsWorldLocked(avatar, *poserJoint, worldLocked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1449,8 +1457,7 @@ void FSFloaterPoser::onResetJoint(const LLSD data)
|
|||
refreshScaleSlidersAndSpinners();
|
||||
refreshTrackpadCursor();
|
||||
enableOrDisableRedoAndUndoButton();
|
||||
if (getSavingToBvh())
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onRedoLastChange()
|
||||
|
|
@ -2373,10 +2380,7 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava
|
|||
if (!poserJoint)
|
||||
continue;
|
||||
|
||||
if (considerExternalFormatSaving)
|
||||
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint));
|
||||
else
|
||||
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue("");
|
||||
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint));
|
||||
|
||||
if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint))
|
||||
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
|
||||
|
|
@ -2393,6 +2397,9 @@ std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPose
|
|||
if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint))
|
||||
return tryGetString("icon_rotation_is_world_locked");
|
||||
|
||||
if (!getSavingToBvh())
|
||||
return "";
|
||||
|
||||
if (joint.boneType() == COL_VOLUMES)
|
||||
return tryGetString("icon_rotation_does_not_export");
|
||||
|
||||
|
|
@ -2678,3 +2685,29 @@ bool FSFloaterPoser::getSavingToBvh()
|
|||
}
|
||||
|
||||
void FSFloaterPoser::onClickSavingToBvh() { refreshTextHighlightingOnJointScrollLists(); }
|
||||
|
||||
void FSFloaterPoser::onClickLockWorldRotBtn()
|
||||
{
|
||||
auto selectedJoints = getUiSelectedPoserJoints();
|
||||
if (selectedJoints.size() < 1)
|
||||
return;
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
for (auto item : selectedJoints)
|
||||
{
|
||||
bool currentlyPosingJoint = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
|
||||
if (!currentlyPosingJoint)
|
||||
continue;
|
||||
|
||||
bool newLockState = !mPoserAnimator.getRotationIsWorldLocked(avatar, *item);
|
||||
mPoserAnimator.setRotationIsWorldLocked(avatar, *item, newLockState);
|
||||
}
|
||||
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ public:
|
|||
void onCommitSpinner(const LLUICtrl* spinner, const S32 ID);
|
||||
void onCommitSlider(const LLUICtrl* slider, const S32 id);
|
||||
void onClickSymmetrize(const S32 ID);
|
||||
void onClickLockWorldRotBtn();
|
||||
|
||||
// UI Refreshments
|
||||
void refreshRotationSlidersAndSpinners();
|
||||
|
|
@ -490,6 +491,7 @@ public:
|
|||
LLButton* mFlipJointBtn{ nullptr };
|
||||
LLButton* mRecaptureBtn{ nullptr };
|
||||
LLButton* mTogglePosingBonesBtn{ nullptr };
|
||||
LLButton* mToggleLockWorldRotBtn{ nullptr };
|
||||
LLButton* mToggleMirrorRotationBtn{ nullptr };
|
||||
LLButton* mToggleSympatheticRotationBtn{ nullptr };
|
||||
LLButton* mToggleDeltaModeBtn{ nullptr };
|
||||
|
|
|
|||
|
|
@ -155,14 +155,14 @@ void FSJointPose::recaptureJoint()
|
|||
mCurrentState = FSJointState(joint);
|
||||
}
|
||||
|
||||
void FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
||||
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
||||
{
|
||||
LLJoint* joint = mJointState->getJoint();
|
||||
if (!joint)
|
||||
return;
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.updateFromJoint(joint, zeroBase);
|
||||
return mCurrentState.updateFromJoint(joint, zeroBase);
|
||||
}
|
||||
|
||||
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
|
||||
|
|
@ -241,6 +241,22 @@ bool FSJointPose::userHaseSetBaseRotationToZero() const
|
|||
return mCurrentState.userSetBaseRotationToZero();
|
||||
}
|
||||
|
||||
bool FSJointPose::getWorldRotationLockState() const
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return false;
|
||||
|
||||
return mCurrentState.mRotationIsWorldLocked;
|
||||
}
|
||||
|
||||
void FSJointPose::setWorldRotationLockState(bool newState)
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mCurrentState.mRotationIsWorldLocked = newState;
|
||||
}
|
||||
|
||||
bool FSJointPose::canPerformUndo() const
|
||||
{
|
||||
switch (mLastSetJointStates.size())
|
||||
|
|
|
|||
|
|
@ -161,7 +161,8 @@ 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>
|
||||
void recaptureJointAsDelta(bool zeroBase);
|
||||
/// <returns>The rotation of the public difference between before and after recapture.</returns>
|
||||
LLQuaternion recaptureJointAsDelta(bool zeroBase);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the undo/redo deque.
|
||||
|
|
@ -174,6 +175,18 @@ class FSJointPose
|
|||
/// <returns>True if the user performed some action to specify zero rotation as the base, otherwise false.</returns>
|
||||
bool userHaseSetBaseRotationToZero() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the rotation of a joint has been 'locked' so that its world rotation can remain constant while parent joints change.
|
||||
/// </summary>
|
||||
/// <returns>True if the joint is rotationally locked to the world, otherwise false.</returns>
|
||||
bool getWorldRotationLockState() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the world-rotation of a joint has been 'locked' so that as its parent joints change rotation or position, this joint keeps a constant world rotation.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state for the world-rotation lock.</param>
|
||||
void setWorldRotationLockState(bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// Reverts the position/rotation/scale to their values when the animation begun.
|
||||
/// This treatment is required for certain joints, particularly Collision Volumes and those bones not commonly animated by an AO.
|
||||
|
|
@ -201,16 +214,9 @@ class FSJointPose
|
|||
}
|
||||
|
||||
FSJointState() = default;
|
||||
LLQuaternion mDeltaRotation;
|
||||
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
|
||||
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
|
||||
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
|
||||
void updateRotation(const LLQuaternion& newRotation)
|
||||
{
|
||||
auto inv_base = mBaseRotation;
|
||||
inv_base.conjugate();
|
||||
mDeltaRotation = newRotation * inv_base;
|
||||
};
|
||||
|
||||
void reflectRotation()
|
||||
{
|
||||
|
|
@ -234,6 +240,7 @@ class FSJointPose
|
|||
void resetJoint()
|
||||
{
|
||||
mUserSpecifiedBaseZero = false;
|
||||
mRotationIsWorldLocked = false;
|
||||
mBaseRotation.set(mStartingRotation);
|
||||
mRotation.set(LLQuaternion::DEFAULT);
|
||||
mPosition.setZero();
|
||||
|
|
@ -256,20 +263,24 @@ class FSJointPose
|
|||
joint->setScale(mBaseScale);
|
||||
}
|
||||
|
||||
void updateFromJoint(LLJoint* joint, bool zeroBase)
|
||||
LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase)
|
||||
{
|
||||
if (!joint)
|
||||
return;
|
||||
return LLQuaternion::DEFAULT;
|
||||
|
||||
LLQuaternion initalPublicRot = mRotation;
|
||||
LLQuaternion invRot = mBaseRotation;
|
||||
invRot.conjugate();
|
||||
mRotation = joint->getRotation() * invRot;
|
||||
LLQuaternion newPublicRot = joint->getRotation() * invRot;
|
||||
|
||||
if (zeroBase)
|
||||
zeroBaseRotation();
|
||||
|
||||
mRotation.set(newPublicRot);
|
||||
mPosition.set(joint->getPosition() - mBasePosition);
|
||||
mScale.set(joint->getScale() - mBaseScale);
|
||||
|
||||
return newPublicRot *= ~initalPublicRot;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -284,12 +295,14 @@ class FSJointPose
|
|||
mPosition.set(state->mPosition);
|
||||
mScale.set(state->mScale);
|
||||
mUserSpecifiedBaseZero = state->userSetBaseRotationToZero();
|
||||
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
|
||||
}
|
||||
|
||||
public:
|
||||
LLQuaternion mRotation;
|
||||
LLVector3 mPosition;
|
||||
LLVector3 mScale;
|
||||
bool mRotationIsWorldLocked = false;
|
||||
|
||||
private:
|
||||
LLQuaternion mStartingRotation;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
jointPose->undoLastChange();
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -104,6 +105,10 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
oppositeJointPose->undoLastChange();
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
|
||||
if (oppositePoserJoint)
|
||||
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, false);
|
||||
}
|
||||
|
||||
void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
|
||||
|
|
@ -173,6 +178,7 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
jointPose->redoLastChange();
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, true);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -182,6 +188,10 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
return;
|
||||
|
||||
oppositeJointPose->redoLastChange();
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
|
||||
if (oppositePoserJoint)
|
||||
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, true);
|
||||
}
|
||||
|
||||
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
|
||||
|
|
@ -274,9 +284,23 @@ bool FSPoserAnimator::getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoser
|
|||
if (!jointPose)
|
||||
return false;
|
||||
|
||||
// TODO: FIRE-35769
|
||||
return jointPose->getWorldRotationLockState();
|
||||
}
|
||||
|
||||
return false;
|
||||
void FSPoserAnimator::setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->setWorldRotationLockState(newState);
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const
|
||||
|
|
@ -382,7 +406,9 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
|
||||
LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -491,6 +517,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
|
||||
case DELTAMODE:
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
return;
|
||||
|
||||
case NONE:
|
||||
|
|
@ -500,31 +527,42 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
else
|
||||
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
return;
|
||||
}
|
||||
|
||||
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
|
||||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
LLQuaternion inv_quat;
|
||||
LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
|
||||
switch (deflectionStyle)
|
||||
{
|
||||
case SYMPATHETIC:
|
||||
oppositeJointPose->cloneRotationFrom(jointPose);
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
|
||||
break;
|
||||
|
||||
case SYMPATHETIC_DELTA:
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
|
||||
break;
|
||||
|
||||
case MIRROR:
|
||||
oppositeJointPose->mirrorRotationFrom(jointPose);
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
break;
|
||||
|
||||
case MIRROR_DELTA:
|
||||
inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
|
||||
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation());
|
||||
if (oppositePoserJoint)
|
||||
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -987,3 +1025,98 @@ int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) co
|
|||
|
||||
return depth;
|
||||
}
|
||||
|
||||
void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotationChange)
|
||||
{
|
||||
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(joint->jointName());
|
||||
if (!parentJoint)
|
||||
return;
|
||||
|
||||
LLJoint* pJoint = parentJoint->getJointState()->getJoint();
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, pJoint->getWorldRotation(), rotationChange);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotatedParentWorldRot, LLQuaternion rotationChange)
|
||||
{
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
if (jointPose->getWorldRotationLockState())
|
||||
{
|
||||
LLQuaternion worldRotOfThisJoint = jointPose->getJointState()->getJoint()->getWorldRotation();
|
||||
LLQuaternion differenceInWorldRot = worldRotOfThisJoint * ~rotatedParentWorldRot;
|
||||
LLQuaternion rotDiffInChildFrame = differenceInWorldRot * rotationChange * ~differenceInWorldRot;
|
||||
rotDiffInChildFrame.conjugate();
|
||||
|
||||
jointPose->setPublicRotation(false, rotDiffInChildFrame * jointPose->getPublicRotation());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, rotatedParentWorldRot, rotationChange);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
|
||||
{
|
||||
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
|
||||
{
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return;
|
||||
|
||||
if (jointPose->getWorldRotationLockState())
|
||||
{
|
||||
redo ? jointPose->redoLastChange() : jointPose->undoLastChange();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
|
||||
if (numberOfBvhChildNodes < 1)
|
||||
return;
|
||||
|
||||
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
|
||||
{
|
||||
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
|
||||
if (!nextJoint)
|
||||
continue;
|
||||
|
||||
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -608,6 +608,14 @@ public:
|
|||
/// <returns>True if the joint is maintaining a fixed-rotation in world, otherwise false.</returns>
|
||||
bool getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the world-rotation-lock status for supplied joint for the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <param name="newState">The lock state to apply.</param>
|
||||
void setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
|
||||
/// </summary>
|
||||
|
|
@ -732,6 +740,45 @@ public:
|
|||
/// <returns>The number of generations of descendents the joint has, if none, then zero.</returns>
|
||||
int getChildJointDepth(const FSPoserJoint* joint, int depth) const;
|
||||
|
||||
/// <summary>
|
||||
/// Derotates the first world-locked child joint to the supplied joint.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="rotation">The rotation the supplied joint was/is being changed by.</param>
|
||||
/// <remarks>
|
||||
/// There are two ways to resolve this problem: before the rotation is applied in the PosingMotion (the animation) or after.
|
||||
/// If performed after, a feedback loop is created, because you're noting the world-rotation in one frame, then correcting it back to that in another.
|
||||
/// This implementation works by applying an opposing-rotation to the locked child joint which is corrected for the relative world-rotations of parent and child.
|
||||
/// </remarks>
|
||||
void deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies a de-rotation if it is world-locked.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="parentWorldRot">The world-rotation of the joint that was edited.</param>
|
||||
/// <param name="rotation">The rotation the joint was edit is being changed by.</param>
|
||||
void deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion parentWorldRot,
|
||||
LLQuaternion rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an undo or redo of an edit to the supplied joints world-locked descendants.
|
||||
/// </summary>
|
||||
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
|
||||
void undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
|
||||
|
||||
/// <summary>
|
||||
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies an undo or redo if it is world-locked.
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint which will have the undo or redo performed, if it is world locked.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
|
||||
void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
|
||||
|
||||
/// <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.
|
||||
|
|
|
|||
|
|
@ -1751,6 +1751,20 @@ width="430">
|
|||
<button.commit_callback
|
||||
function="Poser.TogglePosingSelectedBones"/>
|
||||
</button>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="21"
|
||||
layout="topleft"
|
||||
image_overlay="Locked_Icon"
|
||||
image_hover_unselected="Toolbar_Middle_Over"
|
||||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
name="toggle_LockWorldRotation"
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
tool_tip="Toggle rotation world-lock for the selected limb(s). Limbs that are world-locked keep the same rotation in-world when their parent-limbs move. Eg: lock your eyes and turn your head, your eyes keep looking in the same direction."
|
||||
width="21" >
|
||||
</button>
|
||||
<panel
|
||||
follows="left|top|bottom"
|
||||
height="22"
|
||||
|
|
@ -1760,7 +1774,7 @@ width="430">
|
|||
left_pad="0"
|
||||
top_delta="0"
|
||||
name="button_spacer_panel"
|
||||
width="80"/>
|
||||
width="59"/>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="21"
|
||||
|
|
|
|||
Loading…
Reference in New Issue