FIRE-30873: First pass at posing motion

replacing the BD fixed asset
master
Angeldark Raymaker 2024-10-01 22:25:32 +01:00
parent 6fd345d0b7
commit 3fca86bed9
5 changed files with 489 additions and 40 deletions

View File

@ -160,6 +160,7 @@ set(viewer_SOURCE_FILES
fsparticipantlist.cpp
fspose.cpp
fsposeranimator.cpp
fsposingmotion.cpp
fsradar.cpp
fsradarentry.cpp
fsradarlistctrl.cpp

View File

@ -32,10 +32,9 @@
#include "llagent.h"
#include "llvoavatarself.h"
#include <llanimationstates.h>
#include "llkeyframemotion.h"
#include "fsposingmotion.h"
/// <summary>
/// This has turned into a shim-class rather than the business of posing. *shrug*
/// </summary>
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<FSPosingMotion*>(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<FSPosingMotion*>(avatar->createMotion(animationAssetId));
}
return motion;
}
bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar *avatar)

View File

@ -31,6 +31,7 @@
#include "lljointsolverrp3.h"
#include "v3dmath.h"
#include "llcontrolavatar.h"
#include "fsposingMotion.h"
/// <summary>
/// Describes how we will cluster the joints/bones/thingos.
@ -342,12 +343,37 @@ public:
/// <returns>The rotation vector.</returns>
LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, LLQuaternion rotation);
/// <summary>
/// Creates a posing motion for the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to create the posing motion for.</param>
/// <returns>The posing motion, if created, otherwise nullptr.</returns>
/// <remarks>
/// 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.
/// </remarks>
FSPosingMotion* createPosingMotion(LLVOAvatar* avatar);
/// <summary>
/// Gets the poser posing-motion for the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to get the posing motion for.</param>
/// <returns>The posing motion if found, otherwise nullptr.</returns>
FSPosingMotion* getPosingMotion(LLVOAvatar* avatar);
/// <summary>
/// Determines if the avatar can be used.
/// </summary>
/// <param name="avatar">The avatar to test if it is safe to animate.</param>
/// <returns>True if the avatar is safe to manipulate, otherwise false.</returns>
bool isAvatarSafeToUse(LLVOAvatar *avatar);
/// <summary>
/// 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).
/// </summary>
std::map<LLUUID, LLAssetID> _avatarIdToRegisteredAnimationId;
};
#endif // LL_FSPoserAnimator_H

View File

@ -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<LLJointState> 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<FSPosingMotion::FSJointPose>::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<LLJointState> jointState = pose->findJointState(joint);
if (jointState.isNull())
return false;
U32 state = jointState->getUsage();
return (state & POSER_JOINT_STATE);
}

View File

@ -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); }
/// <summary>
/// A class encapsulating the positions/rotations for a joint.
/// </summary>
class FSJointPose
{
std::string _jointName = ""; // expected to be a match to LLJoint.getName() for a joint implementation.
LLQuaternion _targetRotation;
LLVector3 _targetPosition;
LLPointer<LLJointState> _jointState;
public:
/// <summary>
/// Gets the name of the joint.
/// </summary>
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<LLJointState> 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();
/// <summary>
/// Adds the supplied joint to the current animation-state.
/// </summary>
/// <param name="joint">The joint to animate.</param>
void addJointToState(LLJoint *joint);
/// <summary>
/// Removes the supplied joint to the current animation-state.
/// </summary>
/// <param name="joint">The joint to stop animating.</param>
void removeJointFromState(LLJoint *joint);
/// <summary>
/// Queries whether the supplied joint is being animated.
/// </summary>
/// <param name="joint">The joint to query.</param>
bool currentlyPosingJoint(LLJoint *joint);
/// <summary>
/// Removes the current joint state, and adds a new one.
/// </summary>
void setJointState(LLJoint* joint, U32 state);
/// <summary>
/// Gets the joint pose by name.
/// </summary>
/// <param name="name">The name of the joint to get the pose for.</param>
/// <returns>The matching joint pose, if found, otherwise null.</returns>
FSJointPose* getJointPoseByJointName(std::string name);
/// <summary>
/// Gets the motion identity for this animation.
/// </summary>
/// <returns>The unique, per-session motion identity.</returns>
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<FSJointPose> _jointPoses;
};
#endif // LL_LLKEYFRAMEMOTION_H