phoenix-firestorm/indra/newview/fsposestate.cpp

412 lines
13 KiB
C++

#include "fsposestate.h"
#include "llinventorymodel.h" // gInventory
std::map<LLUUID, std::vector<FSPoseState::fsMotionState>> FSPoseState::sMotionStates;
std::map<LLUUID, int> FSPoseState::sCaptureOrder;
std::map<LLUUID, bool> FSPoseState::sMotionStatesOwnedByMe;
void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
{
if (!avatar)
return;
sCaptureOrder[avatar->getID()] = 0;
int animNumber = 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.motionId = anim_it->first;
newState.lastUpdateTime = motion->getLastUpdateTime();
newState.captureOrder = 0;
newState.inLayerOrder = animNumber++;
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
sMotionStates[avatar->getID()].push_back(newState);
}
}
void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, const std::vector<S32>& jointNumbersRecaptured)
{
if (!avatar || !posingMotion)
return;
sCaptureOrder[avatar->getID()]++;
S32 animNumber = 0;
// if an animation for avatar is a subset of jointNumbersRecaptured, 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();)
{
if (vector2IsSubsetOfVector1(jointNumbersRecaptured, (*it).jointNumbersAnimated))
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, jointNumbersRecaptured))
continue;
bool foundMatch = false;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{
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 = motionIdMatches && updateTimesMatch;
if (foundMatch)
break;
}
if (foundMatch)
continue;
fsMotionState newState;
newState.motionId = anim_it->first;
newState.lastUpdateTime = motion->getLastUpdateTime();
newState.jointNumbersAnimated = jointNumbersRecaptured;
newState.captureOrder = sCaptureOrder[avatar->getID()];
newState.inLayerOrder = animNumber++;
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
sMotionStates[avatar->getID()].push_back(newState);
}
}
void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
{
if (!avatar)
return;
sMotionStates[avatar->getID()].clear();
}
void FSPoseState::writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord)
{
if (!avatar)
return;
S32 animNumber = 0;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); ++it)
{
if (!ignoreOwnership && !it->gAgentOwnsPose)
{
if (it->requeriedAssetInventory)
continue;
it->gAgentOwnsPose = canSaveMotionId(avatar, it->motionId);
it->requeriedAssetInventory = true;
if (!it->gAgentOwnsPose)
continue;
}
std::string uniqueAnimId = "poseState" + std::to_string(animNumber++);
(*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString();
(*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime;
(*saveRecord)[uniqueAnimId]["jointNumbersAnimated"] = encodeVectorToString(it->jointNumbersAnimated);
(*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder;
(*saveRecord)[uniqueAnimId]["inLayerOrder"] = it->inLayerOrder;
}
}
void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, 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;
if (control_map.has("animationId"))
{
std::string const name = control_map["animationId"].asString();
LLUUID animId;
if (LLUUID::parseUUID(name, &animId))
{
newState.motionId = animId;
newState.gAgentOwnsPose = ignoreOwnership || canSaveMotionId(avatar, animId);
if (ignoreOwnership)
sMotionStatesOwnedByMe[animId] = true;
}
}
if (control_map.has("lastUpdateTime"))
newState.lastUpdateTime = (F32)control_map["lastUpdateTime"].asReal();
if (control_map.has("jointNumbersAnimated"))
newState.jointNumbersAnimated = decodeStringToVector(control_map["jointNumbersAnimated"].asString());
if (control_map.has("captureOrder"))
newState.captureOrder = control_map["captureOrder"].asInteger();
if (control_map.has("inLayerOrder"))
newState.inLayerOrder = control_map["inLayerOrder"].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());
S32 lastCaptureOrder = 0;
bool needPriorityReset = false;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{
needPriorityReset = it->captureOrder > lastCaptureOrder;
if (it->motionApplied)
continue;
LLKeyframeMotion* kfm = dynamic_cast<LLKeyframeMotion*>(avatar->findMotion(it->motionId));
if (kfm)
{
if (needPriorityReset)
{
lastCaptureOrder = it->captureOrder;
resetPriorityForCaptureOrder(avatar, posingMotion, lastCaptureOrder);
}
it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNumbersAnimated);
}
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, S32 captureOrder)
{
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{
if (it->jointNumbersAnimated.empty())
continue;
if (it->motionApplied)
continue;
if (it->captureOrder != captureOrder)
continue;
posingMotion->resetBonePriority(it->jointNumbersAnimated);
}
}
bool FSPoseState::canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
{
if (!gAgentAvatarp || gAgentAvatarp.isNull())
return false;
if (sMotionStatesOwnedByMe[motionId])
return true;
// does the animation exist in inventory
LLInventoryItem* item = gInventory.getItem(motionId);
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
{
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
if (!avatarPlayingMotionId)
return false;
if (avatarPlayingMotionId->getID() == gAgentAvatarp->getID())
return motionIdIsAgentAnimationSource(motionId);
return motionIdIsFromPrimAgentOwnsAgentIsSittingOn(avatarPlayingMotionId, motionId);
}
bool FSPoseState::motionIdIsAgentAnimationSource(LLAssetID motionId)
{
if (!gAgentAvatarp || gAgentAvatarp.isNull())
return false;
for (const auto& [anim_object_id, anim_anim_id] : gAgentAvatarp->mAnimationSources)
{
if (anim_anim_id != motionId)
continue;
// is the item that started the anim in inventory
LLInventoryItem* item = gInventory.getItem(anim_object_id);
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
{
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
// is the item that start the animation in-world
LLViewerObject* object = gObjectList.findObject(anim_object_id);
if (object && object->permYouOwner())
{
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
}
return false;
}
bool FSPoseState::motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
{
if (!avatarPlayingMotionId)
return false;
const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(avatarPlayingMotionId->getRoot());
if (!agentRoot)
return false;
const LLUUID& assetIdTheyAreSittingOn = agentRoot->getID();
if (assetIdTheyAreSittingOn == avatarPlayingMotionId->getID())
return false; // they are not sitting on a thing
LLViewerObject* object = gObjectList.findObject(assetIdTheyAreSittingOn);
if (!object || !object->permYouOwner())
return false; // gAgent does not own what they are sitting on
if (object->isInventoryPending())
return false;
if (object->isInventoryDirty() || !object->getInventoryRoot())
{
object->requestInventory();
return false; // whatever they are sitting on, we don't have the inventory list for yet
}
LLInventoryItem* item = object->getInventoryItemByAsset(motionId, LLAssetType::AT_ANIMATION);
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
{
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
return false;
}
bool FSPoseState::vector2IsSubsetOfVector1(std::vector<S32> newRecapture, std::vector<S32> oldRecapture)
{
if (newRecapture.empty())
return false;
if (oldRecapture.empty())
return false;
if (newRecapture.size() < oldRecapture.size())
return false;
for (S32 number : oldRecapture)
if (std::find(newRecapture.begin(), newRecapture.end(), number) == newRecapture.end())
return false;
return true;
}
std::string FSPoseState::encodeVectorToString(const std::vector<S32>& vector)
{
std::string encoded = "";
if (vector.empty())
return encoded;
for (S32 numberToEncode : vector)
{
if (numberToEncode > 251) // max 216 at time of writing
continue;
S32 number = numberToEncode;
if (number >= 189)
{
encoded += "~";
number -= 189;
}
if (number >= 126)
{
encoded += "}";
number -= 126;
}
if (number >= 63)
{
encoded += "|";
number -= 63;
}
encoded += char(number + int('?'));
}
return encoded;
}
std::vector<S32> FSPoseState::decodeStringToVector(std::string_view vector)
{
std::vector<S32> decoded;
if (vector.empty())
return decoded;
S32 number = 0;
for (char ch : vector)
{
if (ch > '~' || ch < '?')
continue;
if (ch == '~')
{
number += 189;
continue;
}
if (ch == '}')
{
number += 126;
continue;
}
if (ch == '|')
{
number += 63;
continue;
}
number -= int('?');
number += S32(ch);
decoded.push_back(number);
number = 0;
}
return decoded;
}