diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 181dbec2bb..ca52fa1203 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -160,6 +160,7 @@ set(viewer_SOURCE_FILES fsparticipantlist.cpp fspose.cpp fsposeranimator.cpp + fsposingmotion.cpp fsradar.cpp fsradarentry.cpp fsradarlistctrl.cpp diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index b5a44f76b0..893a23d341 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -32,10 +32,9 @@ #include "llagent.h" #include "llvoavatarself.h" #include +#include "llkeyframemotion.h" +#include "fsposingmotion.h" -/// -/// This has turned into a shim-class rather than the business of posing. *shrug* -/// FSPoserAnimator::FSPoserAnimator() {} FSPoserAnimator::~FSPoserAnimator() {} @@ -44,14 +43,18 @@ bool FSPoserAnimator::isPosingAvatarJoint(LLVOAvatar *avatar, FSPoserJoint joint if (!isAvatarSafeToUse(avatar)) return false; - if (!motion || motion->isStopped()) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return false; + + if (posingMotion->isStopped()) return false; LLJoint* avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); if (!avJoint) return false; - return motion->currentlyPosingJoint(avJoint); + return posingMotion->currentlyPosingJoint(avJoint); } void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar *avatar, FSPoserJoint joint, bool shouldPose) @@ -63,7 +66,11 @@ void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar *avatar, FSPoserJoint join if (arePosing && shouldPose || !arePosing && !shouldPose) // could !XOR, but this is readable return; - if (!motion || motion->isStopped()) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + if (posingMotion->isStopped()) return; LLJoint* avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); @@ -71,9 +78,9 @@ void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar *avatar, FSPoserJoint join return; if (shouldPose) - motion->addJointToState(avJoint); + posingMotion->addJointToState(avJoint); else - motion->removeJointFromState(avJoint); + posingMotion->removeJointFromState(avJoint); } void FSPoserAnimator::resetAvatarJoint(LLVOAvatar *avatar, FSPoserJoint joint) @@ -81,7 +88,11 @@ void FSPoserAnimator::resetAvatarJoint(LLVOAvatar *avatar, FSPoserJoint joint) if (!isAvatarSafeToUse(avatar)) return; - if (!motion || motion->isStopped()) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + if (posingMotion->isStopped()) return; LLJoint* avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); @@ -97,11 +108,15 @@ LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar *avatar, FSPoserJoint joi if (!isAvatarSafeToUse(avatar)) return pos; - LLJoint* avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); - if (!avJoint) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) return pos; - pos = avJoint->getTargetPosition(); + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + if (!jointPose) + return pos; + + pos = jointPose->getTargetPosition(); return pos; } @@ -117,12 +132,15 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar *avatar, const FSPoserJoint *j if (jn.empty()) return; - JointKey key = JointKey::construct(jn); - LLJoint *avJoint = avatar->getJoint(key); - if (!avJoint) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) return; - avJoint->setTargetPosition(position); + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(jn); + if (!jointPose) + return; + + jointPose->setTargetPosition(position); } LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar *avatar, FSPoserJoint joint, E_BoneAxisTranslation translation, S32 negation, bool forRecapture) @@ -131,11 +149,27 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar *avatar, FSPoserJoint joi if (!isAvatarSafeToUse(avatar)) return vec3; - LLJoint *avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); - if (!avJoint) - return vec3; + LLQuaternion rot; + if (forRecapture) + { + LLJoint* avJoint = avatar->getJoint(JointKey::construct(joint.jointName())); + if (!avJoint) + return vec3; - LLQuaternion rot = forRecapture ? avJoint->getRotation() : avJoint->getTargetRotation(); + rot = avJoint->getRotation(); + } + else + { + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return vec3; + + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); + if (!jointPose) + return vec3; + + rot = jointPose->getTargetRotation(); + } return translateRotationFromQuaternion(translation, negation, rot); } @@ -148,30 +182,34 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar *avatar, const FSPoserJoint *j if (!joint) return; - LLJoint *avJoint = avatar->getJoint(JointKey::construct(joint->jointName())); - if (!avJoint) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName()); + if (!jointPose) return; LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation); - avJoint->setTargetRotation(rot_quat); + jointPose->setTargetRotation(rot_quat); if (style == NONE) return; - LLJoint *oppositeJoint = avatar->getJoint(JointKey::construct(joint->mirrorJointName())); - if (!oppositeJoint) + FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName()); + if (!oppositeJointPose) return; LLQuaternion inv_quat; switch (style) { case SYMPATHETIC: - oppositeJoint->setTargetRotation(rot_quat); + oppositeJointPose->setTargetRotation(rot_quat); break; case MIRROR: inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]); - oppositeJoint->setTargetRotation(inv_quat); + oppositeJointPose->setTargetRotation(inv_quat); break; default: @@ -187,26 +225,32 @@ void FSPoserAnimator::reflectJoint(LLVOAvatar *avatar, const FSPoserJoint *joint if (!joint) return; - LLJoint *avJoint = avatar->getJoint(JointKey::construct(joint->jointName())); - if (!avJoint) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) return; - LLJoint *oppositeJoint = avatar->getJoint(JointKey::construct(joint->mirrorJointName())); - if (!oppositeJoint) + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName()); + if (!jointPose) + return; + + FSPosingMotion::FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName()); + if (!oppositeJointPose) + return; + if (!oppositeJointPose) { - LLQuaternion rot_quat = avJoint->getTargetRotation(); + LLQuaternion rot_quat = jointPose->getTargetRotation(); LLQuaternion inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]); - avJoint->setTargetRotation(inv_quat); + jointPose->setTargetRotation(inv_quat); return; } - LLQuaternion first_quat = avJoint->getTargetRotation(); + LLQuaternion first_quat = jointPose->getTargetRotation(); LLQuaternion first_inv = LLQuaternion(-first_quat.mQ[VX], first_quat.mQ[VY], -first_quat.mQ[VZ], first_quat.mQ[VW]); - LLQuaternion second_quat = oppositeJoint->getTargetRotation(); + LLQuaternion second_quat = oppositeJointPose->getTargetRotation(); LLQuaternion second_inv = LLQuaternion(-second_quat.mQ[VX], second_quat.mQ[VY], -second_quat.mQ[VZ], second_quat.mQ[VW]); - avJoint->setTargetRotation(second_inv); - oppositeJoint->setTargetRotation(first_inv); + jointPose->setTargetRotation(second_inv); + oppositeJointPose->setTargetRotation(first_inv); } void FSPoserAnimator::flipEntirePose(LLVOAvatar *avatar) @@ -385,12 +429,17 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar *avatar) if (!isAvatarSafeToUse(avatar)) return false; - if (!motion || motion->isStopped()) + FSPosingMotion* posingMotion = createPosingMotion(avatar); + if (!posingMotion) + return false; + + if (posingMotion->isStopped()) { if (avatar->isSelf()) gAgent.stopFidget(); avatar->startDefaultMotions(); + avatar->startMotion(posingMotion->motionId()); // TODO: scrape motion state prior to edit, facilitating reset @@ -405,6 +454,11 @@ void FSPoserAnimator::stopPosingAvatar(LLVOAvatar *avatar) if (!avatar || avatar->isDead()) return; + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + avatar->stopMotion(posingMotion->motionId()); } bool FSPoserAnimator::isPosingAvatar(LLVOAvatar* avatar) @@ -412,10 +466,41 @@ bool FSPoserAnimator::isPosingAvatar(LLVOAvatar* avatar) if (!isAvatarSafeToUse(avatar)) return false; - if (!motion) + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) return false; - return !motion->isStopped(); + return !posingMotion->isStopped(); +} + +FSPosingMotion* FSPoserAnimator::getPosingMotion(LLVOAvatar* avatar) +{ + if (!isAvatarSafeToUse(avatar)) + return nullptr; + + if (_avatarIdToRegisteredAnimationId.find(avatar->getID()) == _avatarIdToRegisteredAnimationId.end()) + return nullptr; + + return dynamic_cast(avatar->findMotion(_avatarIdToRegisteredAnimationId[avatar->getID()])); +} + +FSPosingMotion* FSPoserAnimator::createPosingMotion(LLVOAvatar* avatar) +{ + FSPosingMotion* motion = getPosingMotion(avatar); + + if (!motion) + { + LLTransactionID mTransactionID; + mTransactionID.generate(); + LLAssetID animationAssetId = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + + if (avatar->registerMotion(animationAssetId, FSPosingMotion::create)) + _avatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId; + + return dynamic_cast(avatar->createMotion(animationAssetId)); + } + + return motion; } bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar *avatar) diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 43246ad11e..e771dab8c7 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -31,6 +31,7 @@ #include "lljointsolverrp3.h" #include "v3dmath.h" #include "llcontrolavatar.h" +#include "fsposingMotion.h" /// /// Describes how we will cluster the joints/bones/thingos. @@ -342,12 +343,37 @@ public: /// The rotation vector. LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, LLQuaternion rotation); + /// + /// Creates a posing motion for the supplied avatar. + /// + /// The avatar to create the posing motion for. + /// The posing motion, if created, otherwise nullptr. + /// + /// When a pose is created for the avatar, it is 'registered' with their character for use later on. + /// Thus we start & stop posing the same animation. + /// + FSPosingMotion* createPosingMotion(LLVOAvatar* avatar); + + /// + /// Gets the poser posing-motion for the supplied avatar. + /// + /// The avatar to get the posing motion for. + /// The posing motion if found, otherwise nullptr. + FSPosingMotion* getPosingMotion(LLVOAvatar* avatar); + /// /// Determines if the avatar can be used. /// /// The avatar to test if it is safe to animate. /// True if the avatar is safe to manipulate, otherwise false. bool isAvatarSafeToUse(LLVOAvatar *avatar); + + /// + /// Maps the avatar's ID to the animation registered to them. + /// Thus we start/stop the same animation. + /// An avatar's animation exists so long as their session does, and there is consideration for renewal (like if they relog/crash). + /// + std::map _avatarIdToRegisteredAnimationId; }; #endif // LL_FSPoserAnimator_H diff --git a/indra/newview/fsposingmotion.cpp b/indra/newview/fsposingmotion.cpp new file mode 100644 index 0000000000..f7a03adf83 --- /dev/null +++ b/indra/newview/fsposingmotion.cpp @@ -0,0 +1,166 @@ +/** + * @file fsposingmotion.cpp + * @brief Model for posing your (and other) avatar(s). + * + * $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$ + */ + +#include "fsposingMotion.h" +#include "llcharacter.h" +#include "llagent.h" + +FSPosingMotion::FSPosingMotion(const LLUUID &id) : LLMotion(id), _character(NULL) +{ + mName = "fs_poser_pose"; + _motionID = id; +} + +LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character) +{ + if (!character) + return STATUS_FAILURE; + + _character = character; + _jointPoses.clear(); + + LLJoint* targetJoint; + for (S32 i = 0; (targetJoint = character->getCharacterJoint(i)); ++i) // this seems poorly constrained + { + if (!targetJoint) + continue; + + FSJointPose jointPose = FSJointPose(targetJoint); + _jointPoses.push_back(jointPose); + + addJointState(jointPose.getJointState()); + } + + return STATUS_SUCCESS; +} + +bool FSPosingMotion::onActivate() { return true; } + +bool FSPosingMotion::onUpdate(F32 time, U8* joint_mask) +{ + LLQuaternion targetRotation; + LLQuaternion currentRotation; + LLVector3 currentPosition; + LLVector3 targetPosition; + F32 poseTransitionAmount = 0.0f; // when we change from one position/rotation to another, we do so over time; this documents the amount of transition. + + for (FSJointPose jointPose : _jointPoses) + { + LLJoint* joint = jointPose.getJointState()->getJoint(); + if (!joint) + continue; + + currentRotation = joint->getRotation(); + currentPosition = joint->getPosition(); + targetRotation = jointPose.getTargetRotation(); + targetPosition = jointPose.getTargetPosition(); + + poseTransitionAmount = llclamp(_interpolationTimer.getElapsedTimeF32() / _interpolationTime, 0.0f, 1.0f); + if (currentPosition != targetPosition) + { + currentPosition = lerp(currentPosition, targetPosition, _interpolationTime); + jointPose.getJointState()->setPosition(currentPosition); + } + + if (currentRotation != targetRotation) + { + currentRotation = slerp(_interpolationTime, currentRotation, targetRotation); + jointPose.getJointState()->setRotation(currentRotation); + } + } + + if (_interpolationTimer.getStarted() && poseTransitionAmount >= 1.0f) + _interpolationTimer.stop(); + + return true; +} + +void FSPosingMotion::onDeactivate() {} + +void FSPosingMotion::addJointToState(LLJoint* joint) { setJointState(joint, POSER_JOINT_STATE); } + +void FSPosingMotion::removeJointFromState(LLJoint *joint) { setJointState(joint, 0); } + +void FSPosingMotion::setJointState(LLJoint *joint, U32 state) +{ + if (_jointPoses.size() < 1) + return; + if (!joint) + return; + + LLPose* pose = this->getPose(); + if (!pose) + return; + + LLPointer jointState = pose->findJointState(joint); + if (jointState.isNull()) + return; + + pose->removeJointState(jointState); + FSJointPose *jointPose = getJointPoseByJointName(joint->getName()); + if (!jointPose) + return; + + jointPose->getJointState()->setUsage(state); + addJointState(jointPose->getJointState()); +} + +FSPosingMotion::FSJointPose* FSPosingMotion::getJointPoseByJointName(std::string name) +{ + if (_jointPoses.size() < 1) + return nullptr; + + std::vector::iterator poserJoint_iter; + for (poserJoint_iter = _jointPoses.begin(); poserJoint_iter != _jointPoses.end(); ++poserJoint_iter) + { + if (!boost::iequals(poserJoint_iter->jointName(), name)) + continue; + + return &*poserJoint_iter; + } + + return nullptr; +} + +bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint) +{ + if (_jointPoses.size() < 1) + return false; + + if (!joint) + return false; + + LLPose* pose = this->getPose(); + if (!pose) + return false; + + LLPointer jointState = pose->findJointState(joint); + if (jointState.isNull()) + return false; + + U32 state = jointState->getUsage(); + return (state & POSER_JOINT_STATE); +} diff --git a/indra/newview/fsposingmotion.h b/indra/newview/fsposingmotion.h new file mode 100644 index 0000000000..286c680fd6 --- /dev/null +++ b/indra/newview/fsposingmotion.h @@ -0,0 +1,171 @@ +/** + * @file fsposingmotion.cpp + * @brief Model for posing your (and other) avatar(s). + * + * $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 LL_FSPOSINGMOTION_H +#define LL_FSPOSINGMOTION_H + +//----------------------------------------------------------------------------- +// Header files +//----------------------------------------------------------------------------- +#include "llmotion.h" +#include "lljointsolverrp3.h" +#include "v3dmath.h" + +#define MIN_REQUIRED_PIXEL_AREA_POSING 500.f + +//----------------------------------------------------------------------------- +// class FSPosingMotion +//----------------------------------------------------------------------------- +class FSPosingMotion : + public LLMotion +{ +public: + FSPosingMotion(const LLUUID &id); + virtual ~FSPosingMotion(){}; + +public: + static LLMotion *create(const LLUUID &id) { return new FSPosingMotion(id); } + + /// + /// A class encapsulating the positions/rotations for a joint. + /// + class FSJointPose + { + std::string _jointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation. + LLQuaternion _targetRotation; + LLVector3 _targetPosition; + LLPointer _jointState; + + public: + /// + /// Gets the name of the joint. + /// + std::string jointName() const { return _jointName; } + + LLVector3 getTargetPosition() const { return _targetPosition; } + void setTargetPosition(const LLVector3& pos) { _targetPosition.set(pos) ; } + + LLQuaternion getTargetRotation() const { return _targetRotation; } + void setTargetRotation(const LLQuaternion& rot) { _targetRotation.set(rot); } + + LLPointer getJointState() const { return _jointState; } + + FSJointPose(LLJoint* joint) + { + _jointState = new LLJointState; + _jointState->setJoint(joint); + _jointState->setUsage(POSER_JOINT_STATE); + + _jointName = joint->getName(); + + _targetRotation = joint->getRotation(); + _targetPosition = joint->getPosition(); + } + }; + +public: + virtual bool getLoop() { return TRUE; } + + virtual F32 getDuration() { return 0.0; } + + virtual F32 getEaseInDuration() { return 0.0f; } + + virtual F32 getEaseOutDuration() { return 0.5f; } + + virtual LLJoint::JointPriority getPriority() { return LLJoint::ADDITIVE_PRIORITY; } + + virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; } + + // called to determine when a motion should be activated/deactivated based on avatar pixel coverage + virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_POSING; } + + // run-time (post constructor) initialization, + // called after parameters have been set + // must return true to indicate success and be available for activation + virtual LLMotionInitStatus onInitialize(LLCharacter *character); + + // called when a motion is activated + // must return TRUE to indicate success, or else + // it will be deactivated + virtual bool onActivate(); + + // called per time step + // must return TRUE while it is active, and + // must return FALSE when the motion is completed. + virtual bool onUpdate(F32 time, U8 *joint_mask); + + // called when a motion is deactivated + virtual void onDeactivate(); + + /// + /// Adds the supplied joint to the current animation-state. + /// + /// The joint to animate. + void addJointToState(LLJoint *joint); + + /// + /// Removes the supplied joint to the current animation-state. + /// + /// The joint to stop animating. + void removeJointFromState(LLJoint *joint); + + /// + /// Queries whether the supplied joint is being animated. + /// + /// The joint to query. + bool currentlyPosingJoint(LLJoint *joint); + + /// + /// Removes the current joint state, and adds a new one. + /// + void setJointState(LLJoint* joint, U32 state); + + /// + /// Gets the joint pose by name. + /// + /// The name of the joint to get the pose for. + /// The matching joint pose, if found, otherwise null. + FSJointPose* getJointPoseByJointName(std::string name); + + /// + /// Gets the motion identity for this animation. + /// + /// The unique, per-session motion identity. + LLAssetID motionId() const { return _motionID; } + +private: + static const U32 POSER_JOINT_STATE = LLJointState::POS | LLJointState::ROT /* | LLJointState::SCALE*/; + LLAssetID _motionID; + + const F32 _interpolationTime = 0.25f; + + LLFrameTimer _interpolationTimer; + LLCharacter *_character; + std::vector _jointPoses; +}; + +#endif // LL_LLKEYFRAMEMOTION_H +