phoenix-firestorm/indra/newview/fsposingmotion.cpp

505 lines
13 KiB
C++

/**
* @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 <deque>
#include <boost/algorithm/string.hpp>
#include "fsposingmotion.h"
#include "llcharacter.h"
FSPosingMotion::FSPosingMotion(const LLUUID &id) : LLMotion(id)
{
mName = "fs_poser_pose";
mMotionID = id;
}
LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character)
{
if (!character)
return STATUS_FAILURE;
mJointPoses.clear();
LLJoint* targetJoint;
for (S32 i = 0; (targetJoint = character->getCharacterJoint(i)); ++i)
{
if (!targetJoint)
continue;
FSJointPose jointPose = FSJointPose(targetJoint);
mJointPoses.push_back(jointPose);
addJointState(jointPose.getJointState());
}
for (S32 i = 0; (targetJoint = character->findCollisionVolume(i)); ++i)
{
if (!targetJoint)
continue;
FSJointPose jointPose = FSJointPose(targetJoint, true);
mJointPoses.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;
LLVector3 currentScale;
LLVector3 targetScale;
for (FSJointPose jointPose : mJointPoses)
{
LLJoint* joint = jointPose.getJointState()->getJoint();
if (!joint)
continue;
currentRotation = joint->getRotation();
currentPosition = joint->getPosition();
currentScale = joint->getScale();
targetRotation = jointPose.getTargetRotation();
targetPosition = jointPose.getTargetPosition();
targetScale = jointPose.getTargetScale();
if (currentPosition != targetPosition)
{
currentPosition = lerp(currentPosition, targetPosition, mInterpolationTime);
jointPose.getJointState()->setPosition(currentPosition);
}
if (currentRotation != targetRotation)
{
currentRotation = slerp(mInterpolationTime, currentRotation, targetRotation);
jointPose.getJointState()->setRotation(currentRotation);
}
if (currentScale != targetScale)
{
currentScale = lerp(currentScale, targetScale, mInterpolationTime);
jointPose.getJointState()->setScale(currentScale);
}
}
return true;
}
void FSPosingMotion::onDeactivate() { revertChangesToPositionsScalesAndCollisionVolumes(); }
void FSPosingMotion::revertChangesToPositionsScalesAndCollisionVolumes()
{
for (FSJointPose jointPose : mJointPoses)
{
jointPose.revertJointScale();
jointPose.revertJointPosition();
if (jointPose.isCollisionVolume())
jointPose.revertCollisionVolume();
LLJoint* joint = jointPose.getJointState()->getJoint();
if (!joint)
continue;
addJointToState(joint);
}
}
bool FSPosingMotion::currentlyPosingJoint(FSJointPose* joint)
{
if (!joint)
return false;
LLJoint* avJoint = joint->getJointState()->getJoint();
if (!avJoint)
return false;
return currentlyPosingJoint(avJoint);
}
void FSPosingMotion::addJointToState(FSJointPose* joint)
{
if (!joint)
return;
LLJoint* avJoint = joint->getJointState()->getJoint();
if (!avJoint)
return;
setJointState(avJoint, POSER_JOINT_STATE);
}
void FSPosingMotion::removeJointFromState(FSJointPose* joint)
{
if (!joint)
return;
LLJoint* avJoint = joint->getJointState()->getJoint();
if (!avJoint)
return;
joint->revertJointScale();
joint->revertJointPosition();
if (joint->isCollisionVolume())
joint->revertCollisionVolume();
setJointState(avJoint, 0);
}
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 (mJointPoses.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(const std::string& name)
{
if (mJointPoses.size() < 1)
return nullptr;
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
if (!boost::iequals(poserJoint_iter->jointName(), name))
continue;
return &*poserJoint_iter;
}
return nullptr;
}
bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint)
{
if (mJointPoses.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);
}
constexpr size_t MaximumUndoQueueLength = 20;
/// <summary>
/// The constant time interval, in seconds,
/// </summary>
constexpr std::chrono::duration<double> UndoUpdateInterval = std::chrono::duration<double>(0.3);
void FSPosingMotion::FSJointPose::addLastPositionToUndo()
{
if (mUndonePositionIndex > 0)
{
for (int i = 0; i < mUndonePositionIndex; i++)
mLastSetPositions.pop_front();
mUndonePositionIndex = 0;
}
mLastSetPositions.push_front(mTargetPosition);
while (mLastSetPositions.size() > MaximumUndoQueueLength)
mLastSetPositions.pop_back();
}
void FSPosingMotion::FSJointPose::addLastRotationToUndo()
{
if (mUndoneRotationIndex > 0)
{
for (int i = 0; i < mUndoneRotationIndex; i++)
mLastSetRotations.pop_front();
mUndoneRotationIndex = 0;
}
mLastSetRotations.push_front(mTargetRotation);
while (mLastSetRotations.size() > MaximumUndoQueueLength)
mLastSetRotations.pop_back();
}
void FSPosingMotion::FSJointPose::addLastScaleToUndo()
{
if (mUndoneScaleIndex > 0)
{
for (int i = 0; i < mUndoneScaleIndex; i++)
mLastSetScales.pop_front();
mUndoneScaleIndex = 0;
}
mLastSetScales.push_front(mTargetScale);
while (mLastSetScales.size() > MaximumUndoQueueLength)
mLastSetScales.pop_back();
}
LLVector3 FSPosingMotion::FSJointPose::getCurrentPosition()
{
LLVector3 vec3;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return vec3;
vec3 = joint->getPosition();
return vec3;
}
void FSPosingMotion::FSJointPose::setTargetPosition(const LLVector3& pos)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedPosition;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastPositionToUndo();
mTimeLastUpdatedPosition = std::chrono::system_clock::now();
mTargetPosition.set(pos);
}
LLQuaternion FSPosingMotion::FSJointPose::getCurrentRotation()
{
LLQuaternion quat;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return quat;
quat = joint->getRotation();
return quat;
}
void FSPosingMotion::FSJointPose::setTargetRotation(const LLQuaternion& rot)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedRotation;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastRotationToUndo();
mTimeLastUpdatedRotation = std::chrono::system_clock::now();
mTargetRotation.set(rot);
}
void FSPosingMotion::FSJointPose::applyDeltaRotation(const LLQuaternion& rot)
{
auto timeIntervalSinceLastRotationChange = std::chrono::system_clock::now() - mTimeLastUpdatedRotation;
if (timeIntervalSinceLastRotationChange > UndoUpdateInterval)
addLastRotationToUndo();
mTimeLastUpdatedRotation = std::chrono::system_clock::now();
mTargetRotation = mTargetRotation * rot;
}
LLVector3 FSPosingMotion::FSJointPose::getCurrentScale()
{
LLVector3 vec3;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return vec3;
vec3 = joint->getScale();
return vec3;
}
void FSPosingMotion::FSJointPose::setTargetScale(LLVector3 scale)
{
auto timeIntervalSinceLastScaleChange = std::chrono::system_clock::now() - mTimeLastUpdatedScale;
if (timeIntervalSinceLastScaleChange > UndoUpdateInterval)
addLastScaleToUndo();
mTimeLastUpdatedScale = std::chrono::system_clock::now();
mTargetScale.set(scale);
}
void FSPosingMotion::FSJointPose::undoLastPositionSet()
{
if (mLastSetPositions.empty())
return;
if (mUndonePositionIndex == 0) // at the top of the queue add the current
addLastPositionToUndo();
mUndonePositionIndex++;
mUndonePositionIndex = llclamp(mUndonePositionIndex, 0, mLastSetPositions.size() - 1);
mTargetPosition.set(mLastSetPositions[mUndonePositionIndex]);
}
void FSPosingMotion::FSJointPose::redoLastPositionSet()
{
if (mLastSetPositions.empty())
return;
mUndonePositionIndex--;
mUndonePositionIndex = llclamp(mUndonePositionIndex, 0, mLastSetPositions.size() - 1);
mTargetPosition.set(mLastSetPositions[mUndonePositionIndex]);
if (mUndonePositionIndex == 0)
mLastSetPositions.pop_front();
}
void FSPosingMotion::FSJointPose::undoLastRotationSet()
{
if (mLastSetRotations.empty())
return;
if (mUndoneRotationIndex == 0) // at the top of the queue add the current
addLastRotationToUndo();
mUndoneRotationIndex++;
mUndoneRotationIndex = llclamp(mUndoneRotationIndex, 0, mLastSetRotations.size() - 1);
mTargetRotation.set(mLastSetRotations[mUndoneRotationIndex]);
}
void FSPosingMotion::FSJointPose::redoLastRotationSet()
{
if (mLastSetRotations.empty())
return;
mUndoneRotationIndex--;
mUndoneRotationIndex = llclamp(mUndoneRotationIndex, 0, mLastSetRotations.size() - 1);
mTargetRotation.set(mLastSetRotations[mUndoneRotationIndex]);
if (mUndoneRotationIndex == 0)
mLastSetRotations.pop_front();
}
void FSPosingMotion::FSJointPose::undoLastScaleSet()
{
if (mLastSetScales.empty())
return;
if (mUndoneScaleIndex == 0)
addLastScaleToUndo();
mUndoneScaleIndex++;
mUndoneScaleIndex = llclamp(mUndoneScaleIndex, 0, mLastSetScales.size() - 1);
mTargetScale.set(mLastSetScales[mUndoneScaleIndex]);
}
void FSPosingMotion::FSJointPose::redoLastScaleSet()
{
if (mLastSetScales.empty())
return;
mUndoneScaleIndex--;
mUndoneScaleIndex = llclamp(mUndoneScaleIndex, 0, mLastSetScales.size() - 1);
mTargetScale.set(mLastSetScales[mUndoneScaleIndex]);
if (mUndoneScaleIndex == 0)
mLastSetScales.pop_front();
}
void FSPosingMotion::FSJointPose::revertJointScale()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setScale(mBeginningScale);
}
void FSPosingMotion::FSJointPose::revertJointPosition()
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setPosition(mBeginningPosition);
}
void FSPosingMotion::FSJointPose::revertCollisionVolume()
{
if (!mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
joint->setRotation(mBeginningRotation);
joint->setPosition(mBeginningPosition);
joint->setScale(mBeginningScale);
}
FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume)
{
mJointState = new LLJointState;
mJointState->setJoint(joint);
mJointState->setUsage(POSER_JOINT_STATE);
mJointName = joint->getName();
mIsCollisionVolume = isCollisionVolume;
mBeginningRotation = mTargetRotation = joint->getRotation();
mBeginningPosition = mTargetPosition = joint->getPosition();
mBeginningScale = mTargetScale = joint->getScale();
}