/** * @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: /// /// Captures the current animations posing the supplied avatar and how long they have been playing. /// /// The avatar whose animations are to be captured. void captureMotionStates(LLVOAvatar* avatar); /// /// Updates the stored list of animations posing the avatar. /// /// The avatar whose animations are to be captured. /// The posing motion. /// The names of the joints being recaptured. void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, const std::vector& jointNamesRecaptured); /// /// Removes all current animation states for the supplied avatar. /// /// The avatar whose animations are to be purged. void purgeMotionStates(LLVOAvatar* avatar); /// /// Writes any documented poses for the supplied avatar to the supplied stream. /// /// The avatar whose animations may have been captured. /// Whether to ignore ownership. For use when preparing saveRecord to send to another by collab. /// The record to add to. void writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord); /// /// Restores pose state(s) from the supplied record. /// /// The avatar whose animations may have been captured. /// Whether to ignore ownership. For use when reading a local file. /// The record to read from. void restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose); /// /// Applies the motion states for the supplied avatar to the supplied motion. /// /// The avatar to apply the motion state(s) to. /// The posing motion to apply the state(s) to. /// True if all the motion states for the supplied avatar have been applied, otherwise false. /// /// 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. /// bool applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMotion* posingMotion); private: /// /// A class documenting the state of an animation for an avatar. /// class fsMotionState { public: /// /// The motion ID recorded animating the avatar ID. /// LLAssetID motionId; /// /// The play-time the motionId had progressed until the motion was captured. /// F32 lastUpdateTime = 0.f; /// /// Upon reloading, whether this record has been applied to the avatar. /// bool motionApplied = false; /// /// For non-gAgent, we permit a query of the inventory of a prim they are sitting on. /// Because this involves latency, we may retry ownership checking at save-time. /// bool requeriedAssetInventory = false; /// /// Whether gAgent owns the pose, or the pose was loaded from XML. /// bool gAgentOwnsPose = false; /// /// Represents 'capture layers: how the user layers animations 'on top of' others. /// S32 captureOrder = 0; /// /// Represents in-layer order of capture. /// S32 inLayerOrder = 0; /// /// When reloading, and if not-empty, the bone-numbers this motionId should affect. /// std ::vector jointNumbersAnimated; }; /// /// Resets the priority for the named joints for the supplied posing motion at the supplied capture order. /// /// The avatar being posed by the motion. /// The posing motion. /// The order of the capture. void resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, S32 captureOrder); /// /// Gets whether gAgentID owns, and thus can save information about the supplied motionId. /// /// The avatar playing the supplied motionId. /// The motionId of the animation. /// True if the gAgent owns the motionId, otherwise false. /// /// This only works reliably for self. /// For motions playing on others, the motion needs to be an asset in gAgent's inventory. /// bool canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId); /// /// Examines gAgent's animation source list for the supplied animation Id. /// /// The ID of the motion to query. /// True if gAgent is playing the animation, otherwise false. bool motionIdIsAgentAnimationSource(LLAssetID motionId); /// /// Queries a specific condition of the supplied animation ID. /// /// The avatar to query for. /// The motion ID to query for. /// /// True if the supplied avatar is sitting on an object owned by gAgent, and that object /// contains an animation asset with the same assetId. /// /// /// This is intended to test for a situation a photographer might arrange. /// If you are sitting on photographer's prim, playing photographer's pose, and photographer wants to save their work, /// this allows them to save the Animation ID and state to XML. /// It is intended this be called twice at least, as it does not implement a callback onInventoryLoaded. /// Presently this works fine: first time being when posing starts, second when pose is saved. /// bool motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId); /// /// Tests if all the members of supplied vector2 are members of supplied vector1. /// /// The super-set. /// The possible sub-set. /// True if all members of vector2 are members of vector1, otherwise false. bool vector2IsSubsetOfVector1(std::vector vector1, std::vector vector2); /// /// Two symmetric methods for (de)serializing vectors to both XML and collab-safe short-as-possible strings and back again. /// /// /// Collab-safe means ASCII-printable chars, and delimiter usage does not conflict with Collab's delimiter. /// std::string encodeVectorToString(const std::vector& vector); std::vector decodeStringToVector(std::string_view vector); struct compareByCaptureOrder { bool operator()(const fsMotionState& a, const fsMotionState& b) { if (a.captureOrder < b.captureOrder) return true; // Ascending order if (a.captureOrder == b.captureOrder && a.inLayerOrder < b.inLayerOrder) return true; // Ascending order in layer return false; } }; static std::map> sMotionStates; static std::map sCaptureOrder; static std::map sMotionStatesOwnedByMe; }; #endif // LL_FSPoseState_H