FIRE-35794: First pass at restoring pose state
Updated save version: now saves all the playing poses and their times, making 'diffs' much more useful. Loading attempts to replay the pose at that time; making several tries if needed. These poses restore the 'base' rotation state (position needs work).master
parent
3debcc1ca7
commit
dddce2b568
|
|
@ -167,6 +167,7 @@ set(viewer_SOURCE_FILES
|
|||
fsparticipantlist.cpp
|
||||
fspose.cpp
|
||||
fsposeranimator.cpp
|
||||
fsposestate.cpp
|
||||
fsposingmotion.cpp
|
||||
fsprimfeedauth.cpp
|
||||
fsradar.cpp
|
||||
|
|
@ -1007,6 +1008,7 @@ set(viewer_HEADER_FILES
|
|||
fsparticipantlist.h
|
||||
fspose.h
|
||||
fsposeranimator.h
|
||||
fsposestate.h
|
||||
fsposingmotion.h
|
||||
fsprimfeedauth.h
|
||||
fsradar.h
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "llvoavatarself.h"
|
||||
#include "llinventoryfunctions.h"
|
||||
#include "lltoolcomp.h"
|
||||
#include "llloadingindicator.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -110,6 +111,8 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
|
|||
mCommitCallbackRegistrar.add("Poser.CommitSpinner", [this](LLUICtrl* spinner, const LLSD& data) { onCommitSpinner(spinner, data); });
|
||||
mCommitCallbackRegistrar.add("Poser.CommitSlider", [this](LLUICtrl* slider, const LLSD& data) { onCommitSlider(slider, data); });
|
||||
mCommitCallbackRegistrar.add("Poser.Symmetrize", [this](LLUICtrl*, const LLSD& data) { onClickSymmetrize(data); });
|
||||
|
||||
mLoadPoseTimer = new FSLoadPoseTimer(boost::bind(&FSFloaterPoser::timedReload, this));
|
||||
}
|
||||
|
||||
bool FSFloaterPoser::postBuild()
|
||||
|
|
@ -253,7 +256,6 @@ void FSFloaterPoser::onOpen(const LLSD& key)
|
|||
LLFloater::onOpen(key);
|
||||
}
|
||||
|
||||
|
||||
void FSFloaterPoser::onFocusReceived()
|
||||
{
|
||||
LLEditMenuHandler::gEditMenuHandler = this;
|
||||
|
|
@ -302,6 +304,7 @@ void FSFloaterPoser::onClose(bool app_quitting)
|
|||
}
|
||||
|
||||
disableVisualManipulators();
|
||||
delete mLoadPoseTimer;
|
||||
LLFloater::onClose(app_quitting);
|
||||
}
|
||||
|
||||
|
|
@ -512,9 +515,11 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
{
|
||||
bool savingDiff = !mPoserAnimator.allBaseRotationsAreZero(avatar);
|
||||
LLSD record;
|
||||
record["version"]["value"] = (S32)6;
|
||||
record["version"]["value"] = (S32)7;
|
||||
record["startFromTeePose"]["value"] = !savingDiff;
|
||||
|
||||
mPoserAnimator.savePosingState(avatar, &record);
|
||||
|
||||
LLVector3 rotation, position, scale, zeroVector;
|
||||
bool baseRotationIsZero;
|
||||
|
||||
|
|
@ -523,12 +528,15 @@ 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);
|
||||
bool jointRotMirrored = mPoserAnimator.getRotationIsMirrored(avatar, pj);
|
||||
|
||||
record[bone_name] = bone_name;
|
||||
record[bone_name]["enabled"] = posingThisJoint;
|
||||
record[bone_name] = bone_name;
|
||||
record[bone_name]["enabled"] = posingThisJoint;
|
||||
if (!posingThisJoint)
|
||||
continue;
|
||||
|
||||
record[bone_name]["mirrored"] = jointRotMirrored;
|
||||
|
||||
if (!mPoserAnimator.tryGetJointSaveVectors(avatar, pj, &rotation, &position, &scale, &baseRotationIsZero))
|
||||
continue;
|
||||
|
||||
|
|
@ -563,7 +571,6 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -663,6 +670,8 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
|
|||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
|
||||
mPoserAnimator.updatePosingState(avatar, selectedJoints);
|
||||
|
||||
for (auto item : selectedJoints)
|
||||
{
|
||||
bool currentlyPosing = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
|
||||
|
|
@ -892,14 +901,39 @@ void FSFloaterPoser::onPoseMenuAction(const LLSD& param)
|
|||
else if (loadStyle == "selective_rot")
|
||||
loadType = SELECTIVE_ROT;
|
||||
|
||||
mLoadPoseTimer->tryLoading(poseName, loadType);
|
||||
}
|
||||
|
||||
void FSFloaterPoser::timedReload()
|
||||
{
|
||||
if (!mLoadPoseTimer)
|
||||
return;
|
||||
|
||||
LLVOAvatar* avatar = getUiSelectedAvatar();
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
loadPoseFromXml(avatar, poseName, loadType);
|
||||
onJointTabSelect();
|
||||
refreshJointScrollListMembers();
|
||||
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
|
||||
if (loadPoseFromXml(avatar, mLoadPoseTimer->getPosePath(), mLoadPoseTimer->getLoadMethod()))
|
||||
{
|
||||
setLoadingProgress(false);
|
||||
onJointTabSelect();
|
||||
refreshJointScrollListMembers();
|
||||
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
|
||||
}
|
||||
else
|
||||
{
|
||||
setLoadingProgress(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FSFloaterPoser::setLoadingProgress(bool started)
|
||||
{
|
||||
LLLoadingIndicator* mLoadingIndicator = findChild<LLLoadingIndicator>("progress_indicator");
|
||||
if (!mLoadingIndicator)
|
||||
return;
|
||||
|
||||
mLoadingIndicator->setVisible(started);
|
||||
started ? mLoadingIndicator->start() : mLoadingIndicator->stop();
|
||||
}
|
||||
|
||||
void FSFloaterPoser::onClickLoadLeftHandPose()
|
||||
|
|
@ -1020,14 +1054,15 @@ bool FSFloaterPoser::poseFileStartsFromTeePose(const std::string& poseFileName)
|
|||
return false;
|
||||
}
|
||||
|
||||
void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod)
|
||||
bool FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod)
|
||||
{
|
||||
bool loadSuccess = false;
|
||||
std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY);
|
||||
if (!gDirUtilp->fileExists(pathname))
|
||||
return;
|
||||
return loadSuccess;
|
||||
|
||||
if (!mPoserAnimator.isPosingAvatar(avatar))
|
||||
return;
|
||||
return loadSuccess;
|
||||
|
||||
std::string fullPath =
|
||||
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT);
|
||||
|
|
@ -1049,20 +1084,22 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
bool enabled;
|
||||
bool setJointBaseRotationToZero;
|
||||
bool worldLocked;
|
||||
bool mirroredJoint;
|
||||
S32 version = 0;
|
||||
bool startFromZeroRot = true;
|
||||
|
||||
infile.open(fullPath);
|
||||
if (!infile.is_open())
|
||||
return;
|
||||
return loadSuccess;
|
||||
|
||||
loadSuccess = true;
|
||||
while (!infile.eof())
|
||||
{
|
||||
S32 lineCount = LLSDSerialize::fromXML(pose, infile);
|
||||
if (lineCount == LLSDParser::PARSE_FAILURE)
|
||||
{
|
||||
LL_WARNS("Posing") << "Failed to parse file: " << poseFileName << LL_ENDL;
|
||||
return;
|
||||
return loadSuccess;
|
||||
}
|
||||
|
||||
for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr)
|
||||
|
|
@ -1127,13 +1164,22 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
|
|||
|
||||
worldLocked = control_map.has("worldLocked") ? control_map["worldLocked"].asBoolean() : false;
|
||||
mPoserAnimator.setRotationIsWorldLocked(avatar, *poserJoint, worldLocked);
|
||||
|
||||
mirroredJoint = control_map.has("mirrored") ? control_map["mirrored"].asBoolean() : false;
|
||||
mPoserAnimator.setRotationIsMirrored(avatar, *poserJoint, mirroredJoint);
|
||||
}
|
||||
|
||||
if (version > 6)
|
||||
loadSuccess = mPoserAnimator.loadPosingState(avatar, pose);
|
||||
}
|
||||
}
|
||||
catch ( const std::exception & e )
|
||||
{
|
||||
loadSuccess = false;
|
||||
LL_WARNS("Posing") << "Everything caught fire trying to load the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL;
|
||||
}
|
||||
|
||||
return loadSuccess;
|
||||
}
|
||||
|
||||
void FSFloaterPoser::startPosingSelf()
|
||||
|
|
@ -2720,3 +2766,31 @@ void FSFloaterPoser::onClickLockWorldRotBtn()
|
|||
|
||||
refreshTextHighlightingOnJointScrollLists();
|
||||
}
|
||||
|
||||
FSLoadPoseTimer::FSLoadPoseTimer(FSLoadPoseTimer::callback_t callback) : LLEventTimer(0.5f), mCallback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
bool FSLoadPoseTimer::tick()
|
||||
{
|
||||
if (!mAttemptLoading)
|
||||
return false;
|
||||
|
||||
if (mLoadAttempts > 5)
|
||||
return false;
|
||||
|
||||
if (mCallback.empty())
|
||||
return false;
|
||||
|
||||
mCallback();
|
||||
mLoadAttempts++;
|
||||
return false;
|
||||
}
|
||||
|
||||
void FSLoadPoseTimer::tryLoading(std::string filePath, E_LoadPoseMethods loadMethod)
|
||||
{
|
||||
mPoseFullPath = filePath;
|
||||
mLoadType = loadMethod;
|
||||
mLoadAttempts = 0;
|
||||
mAttemptLoading = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class LLLineEditor;
|
|||
class LLScrollListCtrl;
|
||||
class LLSliderCtrl;
|
||||
class LLTabContainer;
|
||||
class FSLoadPoseTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Describes how to load a pose file.
|
||||
|
|
@ -228,11 +229,12 @@ public:
|
|||
bool savePoseToBvh(LLVOAvatar* avatar, const std::string& posePath);
|
||||
void onClickBrowsePoseCache();
|
||||
void onPoseMenuAction(const LLSD& param);
|
||||
void loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod);
|
||||
bool loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod);
|
||||
bool poseFileStartsFromTeePose(const std::string& poseFileName);
|
||||
void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName();
|
||||
void setUiSelectedAvatarSaveFileName(const std::string& saveFileName);
|
||||
bool confirmFileOverwrite(std::string fileName);
|
||||
void timedReload();
|
||||
void setLoadingProgress(bool started);
|
||||
void startPosingSelf();
|
||||
void stopPosingAllAvatars();
|
||||
// visual manipulators control
|
||||
|
|
@ -502,6 +504,8 @@ public:
|
|||
|
||||
LLLineEditor* mPoseSaveNameEditor{ nullptr };
|
||||
|
||||
FSLoadPoseTimer* mLoadPoseTimer;
|
||||
|
||||
LLPanel* mJointsParentPnl{ nullptr };
|
||||
LLPanel* mTrackballPnl{ nullptr };
|
||||
LLPanel* mPositionRotationPnl{ nullptr };
|
||||
|
|
@ -530,4 +534,24 @@ public:
|
|||
LLUICtrl* mScaleZSpnr{ nullptr };
|
||||
};
|
||||
|
||||
class FSLoadPoseTimer : public LLEventTimer
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void()> callback_t;
|
||||
|
||||
FSLoadPoseTimer(callback_t callback);
|
||||
/*virtual*/ bool tick();
|
||||
|
||||
void tryLoading(std::string filePath, E_LoadPoseMethods loadMethod);
|
||||
std::string getPosePath() { return mPoseFullPath; };
|
||||
E_LoadPoseMethods getLoadMethod() { return mLoadType; };
|
||||
|
||||
private:
|
||||
callback_t mCallback;
|
||||
bool mAttemptLoading = false;
|
||||
E_LoadPoseMethods mLoadType = ROT_POS_AND_SCALES;
|
||||
std::string mPoseFullPath;
|
||||
int mLoadAttempts = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ void FSJointPose::setPublicPosition(const LLVector3& pos)
|
|||
{
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.mPosition.set(pos);
|
||||
mCurrentState.mLastChangeWasRotational = false;
|
||||
}
|
||||
|
||||
void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
|
||||
|
|
@ -65,17 +66,22 @@ void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
|
|||
zeroBaseRotation(true);
|
||||
|
||||
mCurrentState.mRotation.set(rot);
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
}
|
||||
|
||||
void FSJointPose::setPublicScale(const LLVector3& scale)
|
||||
{
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.mScale.set(scale);
|
||||
mCurrentState.mLastChangeWasRotational = false;
|
||||
}
|
||||
|
||||
void FSJointPose::undoLastChange()
|
||||
bool FSJointPose::undoLastChange()
|
||||
{
|
||||
mCurrentState = undoLastStateChange(FSJointState(mCurrentState));
|
||||
bool changeType = mCurrentState.mLastChangeWasRotational;
|
||||
mCurrentState = undoLastStateChange(FSJointState(mCurrentState));
|
||||
|
||||
return changeType;
|
||||
}
|
||||
|
||||
void FSJointPose::redoLastChange()
|
||||
|
|
@ -87,6 +93,7 @@ void FSJointPose::resetJoint()
|
|||
{
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.resetJoint();
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
}
|
||||
|
||||
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
|
||||
|
|
@ -153,6 +160,7 @@ void FSJointPose::recaptureJoint()
|
|||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState = FSJointState(joint);
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
}
|
||||
|
||||
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
||||
|
|
@ -162,9 +170,30 @@ LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
|
|||
return LLQuaternion::DEFAULT;
|
||||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
return mCurrentState.updateFromJoint(joint, zeroBase);
|
||||
}
|
||||
|
||||
void FSJointPose::setBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority)
|
||||
{
|
||||
mCurrentState.resetBaseRotation(rotation, priority);
|
||||
}
|
||||
|
||||
void FSJointPose::setBasePosition(LLVector3 position, LLJoint::JointPriority priority)
|
||||
{
|
||||
mCurrentState.resetBasePosition(position, priority);
|
||||
}
|
||||
|
||||
void FSJointPose::setBaseScale(LLVector3 scale, LLJoint::JointPriority priority)
|
||||
{
|
||||
mCurrentState.resetBaseScale(scale, priority);
|
||||
}
|
||||
|
||||
void FSJointPose::setJointPriority(LLJoint::JointPriority priority)
|
||||
{
|
||||
mCurrentState.setPriority(priority);
|
||||
}
|
||||
|
||||
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
|
||||
{
|
||||
if (!oppositeJoint)
|
||||
|
|
@ -177,6 +206,18 @@ void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
|
|||
oppositeJoint->mCurrentState.cloneRotationFrom(tempState);
|
||||
}
|
||||
|
||||
void FSJointPose::swapBaseRotationWith(FSJointPose* oppositeJoint)
|
||||
{
|
||||
if (!oppositeJoint)
|
||||
return;
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
auto tempState = FSJointState(mCurrentState);
|
||||
mCurrentState.cloneBaseRotationFrom(oppositeJoint->mCurrentState);
|
||||
oppositeJoint->mCurrentState.cloneBaseRotationFrom(tempState);
|
||||
}
|
||||
|
||||
void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint)
|
||||
{
|
||||
if (!fromJoint)
|
||||
|
|
@ -184,6 +225,7 @@ void FSJointPose::cloneRotationFrom(FSJointPose* fromJoint)
|
|||
|
||||
addStateToUndo(FSJointState(mCurrentState));
|
||||
mCurrentState.cloneRotationFrom(fromJoint->mCurrentState);
|
||||
mCurrentState.mLastChangeWasRotational = true;
|
||||
}
|
||||
|
||||
void FSJointPose::mirrorRotationFrom(FSJointPose* fromJoint)
|
||||
|
|
@ -208,6 +250,14 @@ void FSJointPose::reflectRotation()
|
|||
mCurrentState.reflectRotation();
|
||||
}
|
||||
|
||||
void FSJointPose::reflectBaseRotation()
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mCurrentState.reflectBaseRotation();
|
||||
}
|
||||
|
||||
void FSJointPose::zeroBaseRotation(bool lockInBvh)
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
|
|
@ -244,20 +294,24 @@ bool FSJointPose::userHasSetBaseRotationToZero() const
|
|||
|
||||
bool FSJointPose::getWorldRotationLockState() const
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return false;
|
||||
|
||||
return mCurrentState.mRotationIsWorldLocked;
|
||||
}
|
||||
|
||||
void FSJointPose::setWorldRotationLockState(bool newState)
|
||||
{
|
||||
if (mIsCollisionVolume)
|
||||
return;
|
||||
|
||||
mCurrentState.mRotationIsWorldLocked = newState;
|
||||
}
|
||||
|
||||
bool FSJointPose::getRotationMirrorState() const
|
||||
{
|
||||
return mCurrentState.mJointRotationIsMirrored;
|
||||
}
|
||||
|
||||
void FSJointPose::setRotationMirrorState(bool newState)
|
||||
{
|
||||
mCurrentState.mJointRotationIsMirrored = newState;
|
||||
}
|
||||
|
||||
bool FSJointPose::canPerformUndo() const
|
||||
{
|
||||
switch (mLastSetJointStates.size())
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ class FSJointPose
|
|||
/// <summary>
|
||||
/// Undoes the last position set, if any.
|
||||
/// </summary>
|
||||
void undoLastChange();
|
||||
/// <returns>true if the change we un-did was rotational.</returns>
|
||||
bool undoLastChange();
|
||||
|
||||
/// <summary>
|
||||
/// Undoes the last position set, if any.
|
||||
|
|
@ -104,6 +105,11 @@ class FSJointPose
|
|||
/// </summary>
|
||||
void reflectRotation();
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the base rotation of the represented joint left-right.
|
||||
/// </summary>
|
||||
void reflectBaseRotation();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the private rotation of the represented joint to zero.
|
||||
/// </summary>
|
||||
|
|
@ -143,6 +149,11 @@ class FSJointPose
|
|||
/// </summary>
|
||||
void swapRotationWith(FSJointPose* oppositeJoint);
|
||||
|
||||
/// <summary>
|
||||
/// Exchanges the base rotations between two joints.
|
||||
/// </summary>
|
||||
void swapBaseRotationWith(FSJointPose* oppositeJoint);
|
||||
|
||||
/// <summary>
|
||||
/// Clones the rotation to this from the supplied joint.
|
||||
/// </summary>
|
||||
|
|
@ -165,6 +176,33 @@ class FSJointPose
|
|||
/// <returns>The rotation of the public difference between before and after recapture.</returns>
|
||||
LLQuaternion recaptureJointAsDelta(bool zeroBase);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base rotation to the supplied rotation if the supplied priority is appropriate.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The base rotation to set; zero is ignored.</param>
|
||||
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
|
||||
void setBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base position to the supplied position if the supplied priority is appropriate.
|
||||
/// </summary>
|
||||
/// <param name="position">The base position to set; zero is ignored.</param>
|
||||
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
|
||||
void setBasePosition(LLVector3 position, LLJoint::JointPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base scale to the supplied scale if the supplied priority is appropriate.
|
||||
/// </summary>
|
||||
/// <param name="scale">The base scale to set; zero is ignored.</param>
|
||||
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
|
||||
void setBaseScale(LLVector3 scale, LLJoint::JointPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the priority of the bone to the supplied value.
|
||||
/// </summary>
|
||||
/// <param name="priority">The new priority of the base rotation.</param>
|
||||
void setJointPriority(LLJoint::JointPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the undo/redo deque.
|
||||
/// </summary>
|
||||
|
|
@ -188,6 +226,18 @@ class FSJointPose
|
|||
/// <param name="newState">The new state for the world-rotation lock.</param>
|
||||
void setWorldRotationLockState(bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the rotation of a joint has been mirrored.
|
||||
/// </summary>
|
||||
/// <returns>True if the joint has been mirrored, otherwise false.</returns>
|
||||
bool getRotationMirrorState() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the rotation of a joint has been mirrored.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state for the mirror.</param>
|
||||
void setRotationMirrorState(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.
|
||||
|
|
@ -212,41 +262,64 @@ class FSJointPose
|
|||
mBaseRotation.set(joint->getRotation());
|
||||
mBasePosition.set(joint->getPosition());
|
||||
mBaseScale.set(joint->getScale());
|
||||
|
||||
mBasePositionFromAnimation.setZero();
|
||||
mBaseScaleFromAnimation.setZero();
|
||||
}
|
||||
|
||||
FSJointState() = default;
|
||||
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
|
||||
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
|
||||
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
|
||||
LLVector3 getTargetPosition() const { return mPosition + mBasePosition + mBasePositionFromAnimation; }
|
||||
LLVector3 getTargetScale() const { return mScale + mBaseScale + mBaseScaleFromAnimation; }
|
||||
|
||||
void reflectRotation()
|
||||
{
|
||||
mBaseRotation.mQ[VX] *= -1;
|
||||
mBaseRotation.mQ[VZ] *= -1;
|
||||
reflectBaseRotation();
|
||||
mRotation.mQ[VX] *= -1;
|
||||
mRotation.mQ[VZ] *= -1;
|
||||
mJointRotationIsMirrored = !mJointRotationIsMirrored;
|
||||
}
|
||||
|
||||
void reflectBaseRotation()
|
||||
{
|
||||
mBaseRotation.mQ[VX] *= -1;
|
||||
mBaseRotation.mQ[VZ] *= -1;
|
||||
}
|
||||
|
||||
void cloneRotationFrom(FSJointState otherState)
|
||||
{
|
||||
mBaseRotation.set(otherState.mBaseRotation);
|
||||
cloneBaseRotationFrom(otherState);
|
||||
mRotation.set(otherState.mRotation);
|
||||
mUserSpecifiedBaseZero = otherState.mUserSpecifiedBaseZero;
|
||||
}
|
||||
|
||||
void cloneBaseRotationFrom(FSJointState otherState)
|
||||
{
|
||||
mBaseRotation.set(otherState.mBaseRotation);
|
||||
}
|
||||
|
||||
bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; }
|
||||
|
||||
void resetJoint()
|
||||
{
|
||||
mUserSpecifiedBaseZero = false;
|
||||
mRotationIsWorldLocked = false;
|
||||
mUserSpecifiedBaseZero = false;
|
||||
mRotationIsWorldLocked = false;
|
||||
mJointRotationIsMirrored = false;
|
||||
mLastChangeWasRotational = true;
|
||||
mBaseRotation.set(mStartingRotation);
|
||||
mRotation.set(LLQuaternion::DEFAULT);
|
||||
mPosition.setZero();
|
||||
mScale.setZero();
|
||||
mBasePositionFromAnimation.setZero();
|
||||
mBaseScaleFromAnimation.setZero();
|
||||
}
|
||||
|
||||
void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; }
|
||||
void zeroBaseRotation()
|
||||
{
|
||||
mBasePriority = LLJoint::LOW_PRIORITY;
|
||||
mBaseRotation = LLQuaternion::DEFAULT;
|
||||
mJointRotationIsMirrored = false;
|
||||
}
|
||||
|
||||
void revertJointToBase(LLJoint* joint) const
|
||||
{
|
||||
|
|
@ -275,12 +348,50 @@ class FSJointPose
|
|||
}
|
||||
|
||||
mRotation.set(newPublicRot);
|
||||
mPosition.set(joint->getPosition() - mBasePosition);
|
||||
mScale.set(joint->getScale() - mBaseScale);
|
||||
mPosition.set(joint->getPosition() - mBasePosition - mBasePositionFromAnimation);
|
||||
mScale.set(joint->getScale() - mBaseScale - mBaseScaleFromAnimation);
|
||||
|
||||
return newPublicRot *= ~initalPublicRot;
|
||||
}
|
||||
|
||||
void resetBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority)
|
||||
{
|
||||
if (priority < mBasePriority)
|
||||
return;
|
||||
|
||||
if (rotation == LLQuaternion::DEFAULT)
|
||||
return;
|
||||
|
||||
LL_WARNS("Posing") << "Loaded rot: " << rotation << " at priority " << priority << LL_ENDL;
|
||||
mBasePriority = priority;
|
||||
mBaseRotation.set(rotation);
|
||||
}
|
||||
|
||||
void resetBasePosition(LLVector3 position, LLJoint::JointPriority priority)
|
||||
{
|
||||
if (priority < mBasePriority)
|
||||
return;
|
||||
|
||||
LL_WARNS("Posing") << "Loaded pos: " << position << " at priority " << priority << LL_ENDL;
|
||||
mBasePriority = priority;
|
||||
mBasePositionFromAnimation.set(position);
|
||||
}
|
||||
|
||||
void resetBaseScale(LLVector3 scale, LLJoint::JointPriority priority)
|
||||
{
|
||||
if (priority < mBasePriority)
|
||||
return;
|
||||
|
||||
if (scale.isExactlyZero())
|
||||
return;
|
||||
|
||||
LL_WARNS("Posing") << "Loaded pos: " << scale << " at priority " << priority << LL_ENDL;
|
||||
mBasePriority = priority;
|
||||
mBaseScaleFromAnimation.set(scale);
|
||||
}
|
||||
|
||||
void setPriority(LLJoint::JointPriority priority) { mBasePriority = priority; }
|
||||
|
||||
private:
|
||||
FSJointState(FSJointState* state)
|
||||
{
|
||||
|
|
@ -288,12 +399,18 @@ class FSJointPose
|
|||
mBaseRotation.set(state->mBaseRotation);
|
||||
mBasePosition.set(state->mBasePosition);
|
||||
mBaseScale.set(state->mBaseScale);
|
||||
mBasePositionFromAnimation.set(state->mBasePositionFromAnimation);
|
||||
mBaseScaleFromAnimation.set(state->mBaseScaleFromAnimation);
|
||||
|
||||
mRotation.set(state->mRotation);
|
||||
mPosition.set(state->mPosition);
|
||||
mScale.set(state->mScale);
|
||||
mUserSpecifiedBaseZero = state->mUserSpecifiedBaseZero;
|
||||
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
|
||||
|
||||
mUserSpecifiedBaseZero = state->mUserSpecifiedBaseZero;
|
||||
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
|
||||
mBasePriority = state->mBasePriority;
|
||||
mJointRotationIsMirrored = state->mJointRotationIsMirrored;
|
||||
mLastChangeWasRotational = state->mLastChangeWasRotational;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -301,6 +418,15 @@ class FSJointPose
|
|||
LLVector3 mPosition;
|
||||
LLVector3 mScale;
|
||||
bool mRotationIsWorldLocked = false;
|
||||
bool mLastChangeWasRotational = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the joint has been mirrored.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used when loading a diff; indicating that the base-rotations, once restored, need to be swapped.
|
||||
/// </remarks>
|
||||
bool mJointRotationIsMirrored = false;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the user has explicitly set the base rotation to zero.
|
||||
|
|
@ -317,7 +443,10 @@ class FSJointPose
|
|||
LLQuaternion mStartingRotation;
|
||||
LLQuaternion mBaseRotation;
|
||||
LLVector3 mBasePosition;
|
||||
LLVector3 mBasePositionFromAnimation;
|
||||
LLVector3 mBaseScale;
|
||||
LLVector3 mBaseScaleFromAnimation;
|
||||
LLJoint::JointPriority mBasePriority = LLJoint::LOW_PRIORITY;
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
if (!jointPose)
|
||||
return;
|
||||
|
||||
jointPose->undoLastChange();
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
|
||||
if (jointPose->undoLastChange())
|
||||
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
|
||||
|
||||
if (style == NONE || style == DELTAMODE)
|
||||
return;
|
||||
|
|
@ -104,7 +104,8 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
|
|||
if (!oppositeJointPose)
|
||||
return;
|
||||
|
||||
oppositeJointPose->undoLastChange();
|
||||
if (!oppositeJointPose->undoLastChange())
|
||||
return;
|
||||
|
||||
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
|
||||
if (oppositePoserJoint)
|
||||
|
|
@ -271,6 +272,38 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
|
|||
}
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::getRotationIsMirrored(LLVOAvatar* avatar, const FSPoserJoint& joint) const
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return false;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return false;
|
||||
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
|
||||
if (!jointPose)
|
||||
return false;
|
||||
|
||||
return jointPose->getRotationMirrorState();
|
||||
}
|
||||
|
||||
void FSPoserAnimator::setRotationIsMirrored(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->setRotationMirrorState(newState);
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
|
|
@ -923,6 +956,56 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
|
|||
jointPose->setPublicScale(scale);
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::loadPosingState(LLVOAvatar* avatar, LLSD pose)
|
||||
{
|
||||
if (!isAvatarSafeToUse(avatar))
|
||||
return false;
|
||||
|
||||
mPosingState.purgeMotionStates(avatar);
|
||||
mPosingState.restoreMotionStates(avatar, pose);
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return false;
|
||||
|
||||
// TODO: do I need to zero all bases first to reset latent rotations?
|
||||
bool loadSuccess = mPosingState.applyMotionStatesToPosingMotion(avatar, posingMotion);
|
||||
if (loadSuccess)
|
||||
applyJointMirrorToBaseRotations(posingMotion);
|
||||
|
||||
return loadSuccess;
|
||||
}
|
||||
|
||||
void FSPoserAnimator::applyJointMirrorToBaseRotations(FSPosingMotion* posingMotion)
|
||||
{
|
||||
for (size_t index = 0; index != PoserJoints.size(); ++index)
|
||||
{
|
||||
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
|
||||
if (!jointPose)
|
||||
continue;
|
||||
|
||||
if (!jointPose->getRotationMirrorState())
|
||||
continue;
|
||||
|
||||
if (PoserJoints[index].dontFlipOnMirror()) // we only flip one side.
|
||||
continue;
|
||||
|
||||
jointPose->reflectBaseRotation();
|
||||
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].mirrorJointName());
|
||||
|
||||
if (!oppositeJointPose)
|
||||
continue;
|
||||
|
||||
oppositeJointPose->reflectBaseRotation();
|
||||
jointPose->swapBaseRotationWith(oppositeJointPose);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoserAnimator::savePosingState(LLVOAvatar* avatar, LLSD* saveRecord)
|
||||
{
|
||||
mPosingState.writeMotionStates(avatar, saveRecord);
|
||||
}
|
||||
|
||||
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const
|
||||
{
|
||||
for (size_t index = 0; index != PoserJoints.size(); ++index)
|
||||
|
|
@ -948,6 +1031,8 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
|
|||
if (avatar->isSelf())
|
||||
gAgent.stopFidget();
|
||||
|
||||
mPosingState.captureMotionStates(avatar);
|
||||
|
||||
avatar->startDefaultMotions();
|
||||
avatar->startMotion(posingMotion->motionId());
|
||||
|
||||
|
|
@ -957,6 +1042,22 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
|
|||
return false;
|
||||
}
|
||||
|
||||
void FSPoserAnimator::updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserAnimator::FSPoserJoint*> jointsRecaptured)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
FSPosingMotion* posingMotion = getPosingMotion(avatar);
|
||||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
std::string jointNamesRecaptured;
|
||||
for (auto item : jointsRecaptured)
|
||||
jointNamesRecaptured += item->jointName();
|
||||
|
||||
mPosingState.updateMotionStates(avatar, posingMotion, jointNamesRecaptured);
|
||||
}
|
||||
|
||||
void FSPoserAnimator::stopPosingAvatar(LLVOAvatar *avatar)
|
||||
{
|
||||
if (!avatar || avatar->isDead())
|
||||
|
|
@ -966,6 +1067,7 @@ void FSPoserAnimator::stopPosingAvatar(LLVOAvatar *avatar)
|
|||
if (!posingMotion)
|
||||
return;
|
||||
|
||||
mPosingState.purgeMotionStates(avatar);
|
||||
avatar->stopMotion(posingMotion->motionId());
|
||||
}
|
||||
|
||||
|
|
@ -1006,7 +1108,6 @@ FSPosingMotion* FSPoserAnimator::findOrCreatePosingMotion(LLVOAvatar* avatar)
|
|||
sAvatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId;
|
||||
|
||||
return dynamic_cast<FSPosingMotion*>(avatar->createMotion(animationAssetId));
|
||||
|
||||
}
|
||||
|
||||
bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const
|
||||
|
|
@ -1118,7 +1219,11 @@ void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& join
|
|||
|
||||
if (jointPose->getWorldRotationLockState())
|
||||
{
|
||||
redo ? jointPose->redoLastChange() : jointPose->undoLastChange();
|
||||
if (redo)
|
||||
jointPose->redoLastChange();
|
||||
else
|
||||
jointPose->undoLastChange();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define LL_FSPoserAnimator_H
|
||||
|
||||
#include "fsposingmotion.h"
|
||||
#include "fsposestate.h"
|
||||
#include "llvoavatar.h"
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -197,27 +198,27 @@ public:
|
|||
/// </remarks>
|
||||
const std::vector<FSPoserJoint> PoserJoints{
|
||||
// head, torso, legs
|
||||
{ "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot", "mSkull" }, "0.000 0.076 0.000" },
|
||||
{ "mNeck", "", BODY, { "mHead" }, "0.000 0.251 -0.010" },
|
||||
{ "mPelvis", "", WHOLEAVATAR, { "mSpine1", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot" }, "0.000000 0.000000 0.000000" },
|
||||
{ "mChest", "", BODY, { "mNeck", "mCollarLeft", "mCollarRight", "mWingsRoot" }, "0.000 0.205 -0.015" },
|
||||
{ "mTorso", "", BODY, { "mSpine3" }, "0.000 0.084 0.000" },
|
||||
{ "mCollarLeft", "mCollarRight", BODY, { "mShoulderLeft" }, "0.085 0.165 -0.021" },
|
||||
{ "mShoulderLeft", "mShoulderRight", BODY, { "mElbowLeft" }, "0.079 0.000 0.000" },
|
||||
{ "mElbowLeft", "mElbowRight", BODY, { "mWristLeft" }, "0.248 0.000 0.000" },
|
||||
{ "mWristLeft", "mWristRight", BODY, { "mHandThumb1Left", "mHandIndex1Left", "mHandMiddle1Left", "mHandRing1Left", "mHandPinky1Left" }, "0.205 0.000 0.000" },
|
||||
{ "mCollarRight", "mCollarLeft", BODY, { "mShoulderRight" }, "-0.085 0.165 -0.021", "", true },
|
||||
{ "mShoulderRight", "mShoulderLeft", BODY, { "mElbowRight" }, "-0.079 0.000 0.000", "", true },
|
||||
{ "mElbowRight", "mElbowLeft", BODY, { "mWristRight" }, "-0.248 0.000 0.000", "", true },
|
||||
{ "mWristRight", "mWristLeft", BODY, { "mHandThumb1Right", "mHandIndex1Right", "mHandMiddle1Right", "mHandRing1Right", "mHandPinky1Right" }, "-0.205 0.000 0.000", "", true },
|
||||
{ "mHipLeft", "mHipRight", BODY, { "mKneeLeft" }, "0.127 -0.041 0.034" },
|
||||
{ "mKneeLeft", "mKneeRight", BODY, { "mAnkleLeft" }, "-0.046 -0.491 -0.001" },
|
||||
{ "mAnkleLeft", "mAnkleRight", BODY, { "mFootLeft" }, "0.001 -0.468 -0.029" },
|
||||
{ "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot", "mSkull", "HEAD" }, "0.000 0.076 0.000" },
|
||||
{ "mNeck", "", BODY, { "mHead", "NECK" }, "0.000 0.251 -0.010" },
|
||||
{ "mPelvis", "", WHOLEAVATAR, { "mSpine1", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot", "PELVIS", "BUTT" }, "0.000000 0.000000 0.000000" },
|
||||
{ "mChest", "", BODY, { "mNeck", "mCollarLeft", "mCollarRight", "mWingsRoot", "CHEST", "LEFT_PEC", "RIGHT_PEC", "UPPER_BACK" }, "0.000 0.205 -0.015" },
|
||||
{ "mTorso", "", BODY, { "mSpine3", "BELLY", "LEFT_HANDLE", "RIGHT_HANDLE", "LOWER_BACK" }, "0.000 0.084 0.000" },
|
||||
{ "mCollarLeft", "mCollarRight", BODY, { "mShoulderLeft", "L_CLAVICLE" }, "0.085 0.165 -0.021" },
|
||||
{ "mShoulderLeft", "mShoulderRight", BODY, { "mElbowLeft", "L_UPPER_ARM" }, "0.079 0.000 0.000" },
|
||||
{ "mElbowLeft", "mElbowRight", BODY, { "mWristLeft", "L_LOWER_ARM" }, "0.248 0.000 0.000" },
|
||||
{ "mWristLeft", "mWristRight", BODY, { "mHandThumb1Left", "mHandIndex1Left", "mHandMiddle1Left", "mHandRing1Left", "mHandPinky1Left", "L_HAND" }, "0.205 0.000 0.000" },
|
||||
{ "mCollarRight", "mCollarLeft", BODY, { "mShoulderRight", "R_CLAVICLE" }, "-0.085 0.165 -0.021", "", true },
|
||||
{ "mShoulderRight", "mShoulderLeft", BODY, { "mElbowRight", "R_UPPER_ARM" }, "-0.079 0.000 0.000", "", true },
|
||||
{ "mElbowRight", "mElbowLeft", BODY, { "mWristRight", "R_LOWER_ARM" }, "-0.248 0.000 0.000", "", true },
|
||||
{ "mWristRight", "mWristLeft", BODY, { "mHandThumb1Right", "mHandIndex1Right", "mHandMiddle1Right", "mHandRing1Right", "mHandPinky1Right", "R_HAND" }, "-0.205 0.000 0.000", "", true },
|
||||
{ "mHipLeft", "mHipRight", BODY, { "mKneeLeft", "L_UPPER_LEG" }, "0.127 -0.041 0.034" },
|
||||
{ "mKneeLeft", "mKneeRight", BODY, { "mAnkleLeft", "L_LOWER_LEG" }, "-0.046 -0.491 -0.001" },
|
||||
{ "mAnkleLeft", "mAnkleRight", BODY, { "mFootLeft", "L_FOOT" }, "0.001 -0.468 -0.029" },
|
||||
{ "mFootLeft", "mFootRight", BODY, { "mToeLeft" }, "0.000 -0.061 0.112" },
|
||||
{ "mToeLeft", "mToeRight", BODY, {}, "0.000 0.000 0.109", "0.000 0.020 0.000" },
|
||||
{ "mHipRight", "mHipLeft", BODY, { "mKneeRight" }, "-0.129 -0.041 0.034", "", true },
|
||||
{ "mKneeRight", "mKneeLeft", BODY, { "mAnkleRight" }, "0.049 -0.491 -0.001", "", true },
|
||||
{ "mAnkleRight", "mAnkleLeft", BODY, { "mFootRight" }, "0.000 -0.468 -0.029", "", true },
|
||||
{ "mHipRight", "mHipLeft", BODY, { "mKneeRight", "R_UPPER_LEG" }, "-0.129 -0.041 0.034", "", true },
|
||||
{ "mKneeRight", "mKneeLeft", BODY, { "mAnkleRight", "R_LOWER_LEG" }, "0.049 -0.491 -0.001", "", true },
|
||||
{ "mAnkleRight", "mAnkleLeft", BODY, { "mFootRight", "R_FOOT" }, "0.000 -0.468 -0.029", "", true },
|
||||
{ "mFootRight", "mFootLeft", BODY, { "mToeRight" }, "0.000 -0.061 0.112", "", true },
|
||||
{ "mToeRight", "mToeLeft", BODY, {}, "0.000 0.000 0.109", "0.000 0.020 0.000", true },
|
||||
|
||||
|
|
@ -617,6 +618,22 @@ public:
|
|||
/// <param name="newState">The lock state to apply.</param>
|
||||
void setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied joint for the supplied avatar has been mirrored.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar owning the supplied joint.</param>
|
||||
/// <param name="joint">The joint to query.</param>
|
||||
/// <returns>True if the joint is maintaining a fixed-rotation in world, otherwise false.</returns>
|
||||
bool getRotationIsMirrored(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mirrored 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 mirror state to apply.</param>
|
||||
void setRotationIsMirrored(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>
|
||||
|
|
@ -690,6 +707,42 @@ public:
|
|||
/// </remarks>
|
||||
void loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the posing state (base rotations) to the supplied avatars posing-motion, from the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">That avatar whose posing state should be loaded.</param>
|
||||
/// <param name="pose">The record to read the posing state from.</param>
|
||||
/// <returns>True if the pose loaded successfully, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// When a save embeds animations that need to be restored at a certain time,
|
||||
/// it can take several frames for the animation to be loaded and ready.
|
||||
/// It may therefore be necessary to attempt this several times.
|
||||
/// </remarks>
|
||||
bool loadPosingState(LLVOAvatar* avatar, LLSD pose);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the posing state for the supplied avatar to the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">That avatar whose posing state should be written.</param>
|
||||
/// <param name="saveRecord">The record to write the posing state to.</param>
|
||||
void savePosingState(LLVOAvatar* avatar, LLSD* saveRecord);
|
||||
|
||||
/// <summary>
|
||||
/// Purges and recaptures the pose state for the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose pose state is to be recapture.</param>
|
||||
/// <param name="jointsRecaptured">The joints which were recaptured.</param>
|
||||
void updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserAnimator::FSPoserJoint*> jointsRecaptured);
|
||||
|
||||
/// <summary>
|
||||
/// Traverses the joints and applies reversals to the base rotations if needed.
|
||||
/// </summary>
|
||||
/// <param name="posingMotion">The posing motion whose pose states require updating.</param>
|
||||
/// <remarks>
|
||||
/// Required after restoring a diff. The base rotations will be in their original arrangment.
|
||||
/// </remarks>
|
||||
void applyJointMirrorToBaseRotations(FSPosingMotion* posingMotion);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Translates a rotation vector from the UI to a Quaternion for the bone.
|
||||
|
|
@ -788,6 +841,8 @@ public:
|
|||
/// Is static, so the animationId is not lost between sessions (such as when the UI floater is closed and reopened).
|
||||
/// </summary>
|
||||
static std::map<LLUUID, LLAssetID> sAvatarIdToRegisteredAnimationId;
|
||||
|
||||
FSPoseState mPosingState;
|
||||
};
|
||||
|
||||
#endif // LL_FSPoserAnimator_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
#include "fsposestate.h"
|
||||
#include "llinventorymodel.h" // gInventory
|
||||
|
||||
std::map<LLUUID, std::vector<FSPoseState::fsMotionState>> FSPoseState::sMotionStates;
|
||||
std::map<LLUUID, int> FSPoseState::sCaptureOrder;
|
||||
|
||||
void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
sCaptureOrder[avatar->getID()] = 0;
|
||||
|
||||
for (auto anim_it = avatar->mPlayingAnimations.begin(); anim_it != avatar->mPlayingAnimations.end(); ++anim_it)
|
||||
{
|
||||
LLKeyframeMotion* motion = dynamic_cast<LLKeyframeMotion*>(avatar->findMotion(anim_it->first));
|
||||
if (!motion)
|
||||
continue;
|
||||
|
||||
fsMotionState newState;
|
||||
newState.avatarId = avatar->getID();
|
||||
newState.motionId = anim_it->first;
|
||||
newState.lastUpdateTime = motion->getLastUpdateTime();
|
||||
newState.captureOrder = 0;
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured)
|
||||
{
|
||||
if (!avatar || !posingMotion)
|
||||
return;
|
||||
|
||||
sCaptureOrder[avatar->getID()]++;
|
||||
|
||||
// if an animation for avatar is a subset of jointNamesRecaptured, delete it
|
||||
// this happens on second/subsequent recaptures; the first recapture is no longer needed
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end();)
|
||||
{
|
||||
bool avatarMatches = (*it).avatarId == avatar->getID();
|
||||
std::string joints = (*it).jointNamesAnimated;
|
||||
bool recaptureMatches = !joints.empty() && !jointNamesRecaptured.empty() && jointNamesRecaptured.find(joints) != std::string::npos;
|
||||
|
||||
if (avatarMatches && recaptureMatches)
|
||||
it = sMotionStates[avatar->getID()].erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
|
||||
for (auto anim_it = avatar->mPlayingAnimations.begin(); anim_it != avatar->mPlayingAnimations.end(); anim_it++)
|
||||
{
|
||||
LLKeyframeMotion* motion = dynamic_cast<LLKeyframeMotion*>(avatar->findMotion(anim_it->first));
|
||||
if (!motion)
|
||||
continue;
|
||||
|
||||
if (!posingMotion->otherMotionAnimatesJoints(motion, jointNamesRecaptured))
|
||||
continue;
|
||||
|
||||
bool foundMatch = false;
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
|
||||
{
|
||||
bool avatarMatches = (*it).avatarId == avatar->getID();
|
||||
bool motionIdMatches = (*it).motionId == anim_it->first;
|
||||
bool updateTimesMatch = (*it).lastUpdateTime == motion->getLastUpdateTime(); // consider when recapturing the same animation at different times for a subset of bones
|
||||
|
||||
foundMatch = avatarMatches && motionIdMatches && updateTimesMatch;
|
||||
if (foundMatch)
|
||||
break;
|
||||
}
|
||||
|
||||
if (foundMatch)
|
||||
continue;
|
||||
|
||||
fsMotionState newState;
|
||||
newState.avatarId = avatar->getID();
|
||||
newState.motionId = anim_it->first;
|
||||
newState.lastUpdateTime = motion->getLastUpdateTime();
|
||||
newState.jointNamesAnimated = jointNamesRecaptured;
|
||||
newState.captureOrder = sCaptureOrder[avatar->getID()];
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
std::vector<fsMotionState>::iterator it;
|
||||
for (it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end();)
|
||||
{
|
||||
if ((*it).avatarId == avatar->getID())
|
||||
it = sMotionStates[avatar->getID()].erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
int animNumber = 0;
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); ++it)
|
||||
{
|
||||
if (it->avatarId != avatar->getID())
|
||||
continue;
|
||||
|
||||
std::string uniqueAnimId = "poseState" + std::to_string(animNumber++);
|
||||
(*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString();
|
||||
(*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime;
|
||||
(*saveRecord)[uniqueAnimId]["jointNamesAnimated"] = it->jointNamesAnimated;
|
||||
(*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder;
|
||||
(*saveRecord)[uniqueAnimId]["playOrder"] = animNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, LLSD pose)
|
||||
{
|
||||
if (!avatar)
|
||||
return;
|
||||
|
||||
sCaptureOrder[avatar->getID()] = 0;
|
||||
|
||||
for (auto itr = pose.beginMap(); itr != pose.endMap(); ++itr)
|
||||
{
|
||||
std::string const& name = itr->first;
|
||||
LLSD const& control_map = itr->second;
|
||||
|
||||
if (!name.starts_with("poseState"))
|
||||
continue;
|
||||
|
||||
fsMotionState newState;
|
||||
newState.avatarId = avatar->getID();
|
||||
|
||||
if (control_map.has("animationId"))
|
||||
{
|
||||
std::string const name = control_map["animationId"].asString();
|
||||
LLUUID animId;
|
||||
if (LLUUID::parseUUID(name, &animId))
|
||||
newState.motionId = animId;
|
||||
}
|
||||
|
||||
if (control_map.has("lastUpdateTime"))
|
||||
newState.lastUpdateTime = (F32)control_map["lastUpdateTime"].asReal();
|
||||
|
||||
if (control_map.has("jointNamesAnimated"))
|
||||
newState.jointNamesAnimated = control_map["jointNamesAnimated"].asString();
|
||||
|
||||
if (control_map.has("captureOrder"))
|
||||
newState.captureOrder = control_map["captureOrder"].asInteger();
|
||||
|
||||
if (newState.captureOrder > sCaptureOrder[avatar->getID()])
|
||||
sCaptureOrder[avatar->getID()] = newState.captureOrder;
|
||||
|
||||
sMotionStates[avatar->getID()].push_back(newState);
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPoseState::applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMotion* posingMotion)
|
||||
{
|
||||
if (!avatar || !posingMotion)
|
||||
return false;
|
||||
|
||||
bool allMotionsApplied = true;
|
||||
|
||||
std::sort(sMotionStates[avatar->getID()].begin(), sMotionStates[avatar->getID()].end(), compareByCaptureOrder());
|
||||
|
||||
int lastCaptureOrder = 0;
|
||||
bool needPriorityReset = false;
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
|
||||
{
|
||||
if (it->avatarId != avatar->getID())
|
||||
continue;
|
||||
|
||||
needPriorityReset = it->captureOrder > lastCaptureOrder;
|
||||
|
||||
if (it->motionApplied)
|
||||
continue;
|
||||
|
||||
if (!avatarCanUsePose(avatar, it->motionId))
|
||||
{
|
||||
it->motionApplied = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
LLKeyframeMotion* kfm = dynamic_cast<LLKeyframeMotion*>(avatar->findMotion(it->motionId));
|
||||
|
||||
if (kfm)
|
||||
{
|
||||
if (needPriorityReset)
|
||||
{
|
||||
lastCaptureOrder = it->captureOrder;
|
||||
LL_WARNS("Posing") << "Resetting priority at cap order: " << lastCaptureOrder << LL_ENDL;
|
||||
resetPriorityForCaptureOrder(avatar, posingMotion, lastCaptureOrder);
|
||||
}
|
||||
|
||||
it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNamesAnimated);
|
||||
}
|
||||
else
|
||||
{
|
||||
avatar->startMotion(it->motionId); // only start if not a kfm; then wait until it casts as a kfm
|
||||
avatar->stopMotion(it->motionId); // only stop if we have used it and we started it
|
||||
}
|
||||
|
||||
allMotionsApplied &= it->motionApplied;
|
||||
}
|
||||
|
||||
return allMotionsApplied;
|
||||
}
|
||||
|
||||
void FSPoseState::resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, int captureOrder)
|
||||
{
|
||||
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
|
||||
{
|
||||
if (it->jointNamesAnimated.empty())
|
||||
continue;
|
||||
if (it->avatarId != avatar->getID())
|
||||
continue;
|
||||
if (it->motionApplied)
|
||||
continue;
|
||||
if (it->captureOrder != captureOrder)
|
||||
continue;
|
||||
|
||||
LL_WARNS("Posing") << "Resetting priority for: " << it->jointNamesAnimated << LL_ENDL;
|
||||
posingMotion->resetBonePriority(it->jointNamesAnimated);
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPoseState::avatarCanUsePose(LLVOAvatar* avatar, LLUUID motionId)
|
||||
{
|
||||
if (!avatar)
|
||||
return true;
|
||||
|
||||
if (!motionId.notNull())
|
||||
return true;
|
||||
|
||||
if (avatar != gAgentAvatarp)
|
||||
return true;
|
||||
|
||||
LLInventoryItem* item = gInventory.getItem(motionId);
|
||||
if (!item)
|
||||
return true;
|
||||
|
||||
return item->getPermissions().getOwner() == avatar->getID();
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/**
|
||||
* @file fsposestate.h
|
||||
* @brief a means to save and restore the instantaneous state of animations posing an avatar.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2025&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
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_FSPoseState_H
|
||||
#define LL_FSPoseState_H
|
||||
|
||||
#include "llvoavatar.h"
|
||||
#include "fsposingmotion.h"
|
||||
|
||||
class FSPoseState
|
||||
{
|
||||
public:
|
||||
FSPoseState() = default;
|
||||
virtual ~FSPoseState() = default;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Captures the current animations posing the supplied avatar and how long they have been playing.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations are to be captured.</param>
|
||||
/// <remarks>
|
||||
/// Only animations owned by the supplied avatar are documented.
|
||||
/// </remarks>
|
||||
void captureMotionStates(LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the stored list of animations posing the avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations are to be captured.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="jointNamesRecaptured">The names of the joints being recaptured.</param>
|
||||
void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all current animation states for the supplied avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations are to be purged.</param>
|
||||
void purgeMotionStates(LLVOAvatar* avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Writes any documented poses for the supplied avatar to the supplied stream.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations may have been captured.</param>
|
||||
/// <param name="saveRecord">The record to add to.</param>
|
||||
void writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord);
|
||||
|
||||
/// <summary>
|
||||
/// Restores pose state(s) from the supplied record.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar whose animations may have been captured.</param>
|
||||
/// <param name="posingMotion">The posing motion.</param>
|
||||
/// <param name="pose">The record to read from.</param>
|
||||
void restoreMotionStates(LLVOAvatar* avatar, LLSD pose);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the motion states for the supplied avatar to the supplied motion.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to apply the motion state(s) to.</param>
|
||||
/// <param name="posingMotion">The posing motion to apply the state(s) to.</param>
|
||||
/// <returns>True if all the motion states for the supplied avatar have been applied, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// In some ways this is like an AO: loading LLKeyframeMotions.
|
||||
/// Once loaded, the LLKeyframeMotion is put at time fsMotionState.lastUpdateTime.
|
||||
/// The joint-rotations for that LLKeyframeMotion are then restored to the base.
|
||||
/// This examines sMotionStates for any avatarId matches; such as after a restoreMotionStates(...).
|
||||
/// This could result in loading assets, thus a particular member of sMotionStates may take several attempts to load.
|
||||
/// Motion(s) that the avatar does not have permissions for are not considered in the return boolean.
|
||||
/// </remarks>
|
||||
bool applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMotion* posingMotion);
|
||||
|
||||
void resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, int captureOrder);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// A class documenting the state of an animation for an avatar.
|
||||
/// </summary>
|
||||
class fsMotionState
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// The avatar ID this record is associated with.
|
||||
/// </summary>
|
||||
LLUUID avatarId;
|
||||
|
||||
/// <summary>
|
||||
/// The motion ID recorded animating the avatar ID.
|
||||
/// </summary>
|
||||
LLAssetID motionId;
|
||||
|
||||
/// <summary>
|
||||
/// The play-time the motionId had progressed until the motion was captured.
|
||||
/// </summary>
|
||||
F32 lastUpdateTime = 0.f;
|
||||
|
||||
/// <summary>
|
||||
/// Upon reloading, whether this record has been applied to the avatar.
|
||||
/// </summary>
|
||||
bool motionApplied = false;
|
||||
|
||||
/// <summary>
|
||||
/// When reloading, larger numbers are loaded last, nesting order and priority.
|
||||
/// Represents recaptures.
|
||||
/// </summary>
|
||||
int captureOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// When reloading, and if not-empty, the names of the bones this motionId should affect.
|
||||
/// </summary>
|
||||
std ::string jointNamesAnimated;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the supplied avatar has ownership of the supplied motion id.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to query for ownership.</param>
|
||||
/// <param name="motionId">The motion to query for ownership.</param>
|
||||
/// <returns>True if the avatar has ownership of the motion, otherwise false.</returns>
|
||||
bool avatarCanUsePose(LLVOAvatar* avatar, LLUUID motionId);
|
||||
|
||||
struct compareByCaptureOrder
|
||||
{
|
||||
bool operator()(const fsMotionState& a, const fsMotionState& b)
|
||||
{
|
||||
if (a.captureOrder < b.captureOrder)
|
||||
return true; // Ascending order
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static std::map <LLUUID, std::vector<fsMotionState>> sMotionStates;
|
||||
static std::map<LLUUID, int> sCaptureOrder;
|
||||
};
|
||||
|
||||
#endif // LL_FSPoseState_H
|
||||
|
|
@ -29,10 +29,11 @@
|
|||
#include "fsposingmotion.h"
|
||||
#include "llcharacter.h"
|
||||
|
||||
FSPosingMotion::FSPosingMotion(const LLUUID &id) : LLMotion(id)
|
||||
FSPosingMotion::FSPosingMotion(const LLUUID& id) : LLKeyframeMotion(id)
|
||||
{
|
||||
mName = "fs_poser_pose";
|
||||
mMotionID = id;
|
||||
mJointMotionList = &dummyMotionList;
|
||||
}
|
||||
|
||||
LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character)
|
||||
|
|
@ -269,6 +270,113 @@ void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
|
|||
joint->zeroBaseRotation(lockInBvh);
|
||||
}
|
||||
|
||||
bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames)
|
||||
{
|
||||
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToLoad);
|
||||
if (!motionToLoadAsFsPosingMotion)
|
||||
return false;
|
||||
|
||||
LLJoint::JointPriority priority = motionToLoad->getPriority();
|
||||
bool motionIsForAllJoints = selectedJointNames.empty();
|
||||
|
||||
LLQuaternion rot;
|
||||
LLVector3 position, scale;
|
||||
bool hasRotation = false, hasPosition = false, hasScale = false;
|
||||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
{
|
||||
std::string jointName = poserJoint_iter->jointName();
|
||||
|
||||
bool motionIsForThisJoint = selectedJointNames.find(jointName) != std::string::npos;
|
||||
if (!motionIsForAllJoints && !motionIsForThisJoint)
|
||||
continue;
|
||||
|
||||
hasRotation = hasPosition = hasScale = false;
|
||||
motionToLoadAsFsPosingMotion->getJointStateAtTime(jointName, timeToLoadAt, &hasRotation, &rot, &hasPosition, &position, &hasScale, &scale);
|
||||
|
||||
if (hasRotation)
|
||||
poserJoint_iter->setBaseRotation(rot, priority);
|
||||
|
||||
//if (hasPosition)
|
||||
// poserJoint_iter->setBasePosition(position, priority);
|
||||
|
||||
//if (hasScale)
|
||||
// poserJoint_iter->setBaseScale(scale, priority);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FSPosingMotion::getJointStateAtTime(std::string jointPoseName, F32 timeToLoadAt,
|
||||
bool* hasRotation, LLQuaternion* jointRotation,
|
||||
bool* hasPosition, LLVector3* jointPosition,
|
||||
bool* hasScale, LLVector3* jointScale)
|
||||
{
|
||||
if (this == NULL || mJointMotionList == nullptr)
|
||||
return;
|
||||
|
||||
for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++)
|
||||
{
|
||||
JointMotion* jm = mJointMotionList->getJointMotion(i);
|
||||
if (!boost::iequals(jointPoseName, jm->mJointName))
|
||||
continue;
|
||||
|
||||
*hasRotation = (jm->mRotationCurve.mNumKeys > 0);
|
||||
if (hasRotation)
|
||||
jointRotation->set(jm->mRotationCurve.getValue(timeToLoadAt, mJointMotionList->mDuration));
|
||||
|
||||
*hasPosition = (jm->mPositionCurve.mNumKeys > 0);
|
||||
if (hasPosition)
|
||||
jointPosition->set(jm->mPositionCurve.getValue(timeToLoadAt, mJointMotionList->mDuration));
|
||||
|
||||
*hasScale = (jm->mScaleCurve.mNumKeys > 0);
|
||||
if (hasScale)
|
||||
jointScale->set(jm->mScaleCurve.getValue(timeToLoadAt, mJointMotionList->mDuration));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPosingMotion::otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames)
|
||||
{
|
||||
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToQuery);
|
||||
if (!motionToLoadAsFsPosingMotion)
|
||||
return false;
|
||||
|
||||
return motionToLoadAsFsPosingMotion->motionAnimatesJoints(recapturedJointNames);
|
||||
}
|
||||
|
||||
bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
|
||||
{
|
||||
if (this == NULL || mJointMotionList == nullptr)
|
||||
return false;
|
||||
|
||||
for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++)
|
||||
{
|
||||
JointMotion* jm = mJointMotionList->getJointMotion(i);
|
||||
if (recapturedJointNames.find(jm->mJointName) == std::string::npos)
|
||||
continue;
|
||||
|
||||
if (jm->mRotationCurve.mNumKeys > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FSPosingMotion::resetBonePriority(std::string boneNamesToReset)
|
||||
{
|
||||
if (boneNamesToReset.empty())
|
||||
return;
|
||||
|
||||
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
|
||||
{
|
||||
std::string jointName = poserJoint_iter->jointName();
|
||||
if (boneNamesToReset.find(jointName) != std::string::npos)
|
||||
poserJoint_iter->setJointPriority(LLJoint::LOW_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPosingMotion::vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const
|
||||
{
|
||||
if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) &&
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "llmotion.h"
|
||||
#include "fsjointpose.h"
|
||||
#include "llkeyframemotion.h"
|
||||
|
||||
#define MIN_REQUIRED_PIXEL_AREA_POSING 500.f
|
||||
|
||||
|
|
@ -39,10 +40,11 @@
|
|||
// class FSPosingMotion
|
||||
//-----------------------------------------------------------------------------
|
||||
class FSPosingMotion :
|
||||
public LLMotion
|
||||
public LLKeyframeMotion
|
||||
{
|
||||
public:
|
||||
FSPosingMotion(const LLUUID &id);
|
||||
FSPosingMotion(const LLKeyframeMotion& kfm) : LLKeyframeMotion{ kfm } { }
|
||||
virtual ~FSPosingMotion(){};
|
||||
|
||||
public:
|
||||
|
|
@ -132,6 +134,58 @@ public:
|
|||
/// <param name="lockInBvh">Whether the joint should be locked if exported to BVH.</param>
|
||||
void setJointBvhLock(FSJointPose* joint, bool lockInBvh);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the rotations of the supplied motion at the supplied time to the base
|
||||
/// </summary>
|
||||
/// <param name="motionToLoad">The motion whose joint rotations (etc) we want to copy to this.</param>
|
||||
/// <param name="timeToLoadAt">The play-time the animation should be advanced to derive the correct joint state.</param>
|
||||
/// <param name="selectedJointNames">If only some of the joints should be animated by this motion, name them here.</param>
|
||||
/// <returns></returns>
|
||||
bool loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the rotation, position and scale for the supplied joint name at the supplied time.
|
||||
/// </summary>
|
||||
/// <param name="jointPoseName">The name of the joint. Example: "mPelvis".</param>
|
||||
/// <param name="timeToLoadAt">The time to get the rotation at.</param>
|
||||
/// <param name="hasRotation">Output of whether the animation has a rotation for the supplied joint name.</param>
|
||||
/// <param name="jointRotation">The output rotation of the named joint.</param>
|
||||
/// <param name="hasPosition">Output of whether the animation has a position for the supplied joint name.</param>
|
||||
/// <param name="jointPosition">The output position of the named joint.</param>
|
||||
/// <param name="hasScale">Output of whether the animation has a scale for the supplied joint name.</param>
|
||||
/// <param name="jointScale">The output scale of the named joint.</param>
|
||||
/// <remarks>
|
||||
/// The most significant thing this method does is provide access to protected properties of some other LLPosingMotion.
|
||||
/// Thus its most common usage would be to access those properties for an arbitrary animation 'from' the poser's instance of one of these.
|
||||
/// </remarks>
|
||||
void getJointStateAtTime(std::string jointPoseName, F32 timeToLoadAt, bool* hasRotation, LLQuaternion* jointRotation,
|
||||
bool* hasPosition, LLVector3* jointPosition, bool* hasScale, LLVector3* jointScale);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the bone priority to zero for the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="boneNamesToReset">The string containg bone names (like mPelvis).</param>
|
||||
void resetBonePriority(std::string boneNamesToReset);
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the supplied motion animates any of the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="motionToQuery">The motion to query.</param>
|
||||
/// <param name="recapturedJointNames">A string containing all of the joint names.</param>
|
||||
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
|
||||
bool otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames);
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the this motion animates any of the joints named in the supplied string.
|
||||
/// </summary>
|
||||
/// <param name="recapturedJointNames">A string containing all of the joint names.</param>
|
||||
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
|
||||
/// <remarks>
|
||||
/// The most significant thing this method does is provide access to protected properties of an LLPosingMotion.
|
||||
/// Thus its most common usage would be to access those properties for an arbitrary animation.
|
||||
/// </remarks>
|
||||
bool motionAnimatesJoints(std::string recapturedJointNames);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// The axial difference considered close enough to be the same.
|
||||
|
|
@ -154,6 +208,11 @@ private:
|
|||
/// </summary>
|
||||
LLAssetID mMotionID;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor and usage requires this not be NULL.
|
||||
/// </summary>
|
||||
JointMotionList dummyMotionList;
|
||||
|
||||
/// <summary>
|
||||
/// The time constant, in seconds, we use for transitioning between one animation-state to another; this affects the 'damping'
|
||||
/// of motion between changes to a joint. 'Constant' in this context is not a reference to the language-idea of 'const' value.
|
||||
|
|
|
|||
|
|
@ -1838,6 +1838,15 @@ width="430">
|
|||
<button.commit_callback
|
||||
function="Poser.BrowseCache"/>
|
||||
</button>
|
||||
<loading_indicator
|
||||
visible="false"
|
||||
follows="left|top"
|
||||
height="20"
|
||||
layout="topleft"
|
||||
name="progress_indicator"
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="20" />
|
||||
<menu_button
|
||||
height="21"
|
||||
follows="top|left"
|
||||
|
|
@ -1856,7 +1865,7 @@ width="430">
|
|||
name="load_poses_button"
|
||||
left_pad="1"
|
||||
top_delta="0"
|
||||
width="95"/>
|
||||
width="85"/>
|
||||
<button
|
||||
height="21"
|
||||
follows="top|left"
|
||||
|
|
@ -1871,7 +1880,7 @@ width="430">
|
|||
image_selected="Toolbar_Middle_Selected"
|
||||
image_unselected="Toolbar_Middle_Off"
|
||||
name="save_poses_button"
|
||||
width="95"
|
||||
width="85"
|
||||
top_delta="0"
|
||||
left_pad="1">
|
||||
<button.commit_callback
|
||||
|
|
|
|||
Loading…
Reference in New Issue