/** * @file fsjointpose.h * @brief Container for the pose of a joint. * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Phoenix Firestorm Viewer Source Code * Copyright (c) 2024 Angeldark Raymaker @ Second Life * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef FS_JOINTPPOSE_H #define FS_JOINTPPOSE_H //----------------------------------------------------------------------------- // Header files //----------------------------------------------------------------------------- #include "llmotion.h" //----------------------------------------------------------------------------- // class FSJointPose //----------------------------------------------------------------------------- class FSJointPose { public: /// /// A class encapsulating the positions/rotations for a joint. /// /// The joint this joint pose represents. /// The default usage the joint should have in a pose. /// Whether the supplied joint is a collision volume. FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume = false); /// /// Gets the name of the joint. /// std::string jointName() const { return mJointName; } /// /// Gets whether this represents a collision volume. /// /// true if the joint is a collision volume, otherwise false. bool isCollisionVolume() const { return mIsCollisionVolume; } /// /// Gets the 'public' position of the joint. /// LLVector3 getPublicPosition() const { return mCurrentState.mPosition; } /// /// Sets the 'public' position of the joint. /// void setPublicPosition(const LLVector3& pos); /// /// Undoes the last position set, if any. /// void undoLastChange(); /// /// Undoes the last position set, if any. /// void redoLastChange(); /// /// Gets the 'public' rotation of the joint. /// LLQuaternion getPublicRotation() const { return mCurrentState.mRotation; } /// /// Sets the 'public' rotation of the joint. /// /// /// '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. /// void setPublicRotation(const LLQuaternion& rot); /// /// Reflects the base and delta rotation of the represented joint left-right. /// void reflectRotation(); /// /// Sets the private rotation of the represented joint to zero. /// void zeroBaseRotation(); /// /// Queries whether the represented joint is zero. /// /// True if the represented joint is zero, otherwise false. bool isBaseRotationZero() const; /// /// Gets whether an undo of this joint may be performed. /// /// true if the joint may have a undo applied, otherwise false. bool canPerformUndo() const; /// /// Gets whether a redo of this joint may be performed. /// /// true if the joint may have a redo applied, otherwise false. bool canPerformRedo() const { return mUndoneJointStatesIndex > 0; } /// /// Gets the 'public' scale of the joint. /// LLVector3 getPublicScale() const { return mCurrentState.mScale; } /// /// Sets the 'public' scale of the joint. /// void setPublicScale(const LLVector3& scale); /// /// Exchanges the rotations between two joints. /// void swapRotationWith(FSJointPose* oppositeJoint); /// /// Clones the rotation to this from the supplied joint. /// void cloneRotationFrom(FSJointPose* fromJoint); /// /// Mirrors the rotation to this from the supplied joint. /// void mirrorRotationFrom(FSJointPose* fromJoint); /// /// Resets the beginning properties of the joint this represents. /// void recaptureJoint(); /// /// Recalculates the delta reltive to the base for a new rotation. /// void recaptureJointAsDelta(); /// /// Clears the undo/redo deque. /// void purgeUndoQueue(); /// /// 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. /// void revertJoint(); LLQuaternion getTargetRotation() const { return mCurrentState.getTargetRotation(); } LLVector3 getTargetPosition() const { return mCurrentState.getTargetPosition(); } LLVector3 getTargetScale() const { return mCurrentState.getTargetScale(); } /// /// Gets the pointer to the jointstate for the joint this represents. /// LLPointer getJointState() const { return mJointState; } class FSJointState { public: FSJointState(LLJoint* joint) { mBaseRotation.set(joint->getRotation()); mBasePosition.set(joint->getPosition()); mBaseScale.set(joint->getScale()); } 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() { mBaseRotation.mQ[VX] *= -1; mBaseRotation.mQ[VZ] *= -1; mRotation.mQ[VX] *= -1; mRotation.mQ[VZ] *= -1; } void cloneRotationFrom(FSJointState otherState) { mBaseRotation.set(otherState.mBaseRotation); mRotation.set(otherState.mRotation); } bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; } void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; } void revertJointToBase(LLJoint* joint) const { if (!joint) return; joint->setRotation(mBaseRotation); joint->setPosition(mBasePosition); joint->setScale(mBaseScale); } void updateFromJoint(LLJoint* joint) { if (!joint) return; LLQuaternion invRot = mBaseRotation; invRot.conjugate(); mRotation = joint->getRotation() * invRot; mPosition.set(joint->getPosition() - mBasePosition); mScale.set(joint->getScale() - mBaseScale); } private: FSJointState(FSJointState* state) { mBaseRotation.set(state->mBaseRotation); mBasePosition.set(state->mBasePosition); mBaseScale.set(state->mBaseScale); mRotation.set(state->mRotation); mPosition.set(state->mPosition); mScale.set(state->mScale); } public: LLQuaternion mRotation; LLVector3 mPosition; LLVector3 mScale; private: LLQuaternion mBaseRotation; LLVector3 mBasePosition; LLVector3 mBaseScale; }; private: std::string mJointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation. LLPointer mJointState{ nullptr }; /// /// Collision Volumes require special treatment when we stop animating an avatar, as they do not revert to their original state /// natively. /// bool mIsCollisionVolume{ false }; std::deque mLastSetJointStates; size_t mUndoneJointStatesIndex = 0; std::chrono::system_clock::time_point mTimeLastUpdatedCurrentState = std::chrono::system_clock::now(); FSJointState mCurrentState; void addStateToUndo(FSJointState stateToAddToUndo); FSJointState undoLastStateChange(FSJointState currentState); FSJointState redoLastStateChange(FSJointState currentState); }; #endif // FS_JOINTPPOSE_H