FIRE-34744: Update save version, update UI

Save all rotations as deltas.
master
Angeldark Raymaker 2024-11-03 21:03:52 +00:00
parent 1e143623c3
commit fddbde3f91
8 changed files with 678 additions and 251 deletions

View File

@ -8056,6 +8056,17 @@
<key>Value</key> <key>Value</key>
<integer>0</integer> <integer>0</integer>
</map> </map>
<key>FSPoserStopPosingWhenClosed</key>
<map>
<key>Comment</key>
<string>Whether to stop animating with the poser when the poser window is closed.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>FSPoserTrackpadSensitivity</key> <key>FSPoserTrackpadSensitivity</key>
<map> <map>
<key>Comment</key> <key>Comment</key>

View File

@ -40,6 +40,7 @@
#include "llviewercontrol.h" #include "llviewercontrol.h"
#include "llviewerwindow.h" #include "llviewerwindow.h"
#include "llwindow.h" #include "llwindow.h"
#include "llvoavatarself.h"
namespace namespace
{ {
@ -52,6 +53,7 @@ constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_";
constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_"; constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_";
constexpr std::string_view POSER_ADVANCEDWINDOWSTATE_SAVE_KEY = "FSPoserAdvancedWindowState"; constexpr std::string_view POSER_ADVANCEDWINDOWSTATE_SAVE_KEY = "FSPoserAdvancedWindowState";
constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity"; constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed";
} // namespace } // namespace
/// <summary> /// <summary>
@ -59,6 +61,7 @@ constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpa
/// The trackpad ordinarily has a range of +1..-1; multiplied by PI, gives PI to -PI, or all 360 degrees of deflection. /// The trackpad ordinarily has a range of +1..-1; multiplied by PI, gives PI to -PI, or all 360 degrees of deflection.
/// </summary> /// </summary>
constexpr F32 NormalTrackpadRangeInRads = F_PI; constexpr F32 NormalTrackpadRangeInRads = F_PI;
bool FSFloaterPoser::mDisableRecaptureUntilStopPosing;
FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key) FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
{ {
@ -76,6 +79,7 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.AdjustTrackPadSensitivity", [this](LLUICtrl*, const LLSD&) { onAdjustTrackpadSensitivity(); }); mCommitCallbackRegistrar.add("Poser.AdjustTrackPadSensitivity", [this](LLUICtrl*, const LLSD&) { onAdjustTrackpadSensitivity(); });
mCommitCallbackRegistrar.add("Poser.PositionSet", [this](LLUICtrl*, const LLSD&) { onAvatarPositionSet(); }); mCommitCallbackRegistrar.add("Poser.PositionSet", [this](LLUICtrl*, const LLSD&) { onAvatarPositionSet(); });
mCommitCallbackRegistrar.add("Poser.SetToTPose", [this](LLUICtrl*, const LLSD&) { onSetAvatarToTpose(); });
mCommitCallbackRegistrar.add("Poser.Advanced.PositionSet", [this](LLUICtrl*, const LLSD&) { onAdvancedPositionSet(); }); mCommitCallbackRegistrar.add("Poser.Advanced.PositionSet", [this](LLUICtrl*, const LLSD&) { onAdvancedPositionSet(); });
mCommitCallbackRegistrar.add("Poser.Advanced.ScaleSet", [this](LLUICtrl*, const LLSD&) { onAdvancedScaleSet(); }); mCommitCallbackRegistrar.add("Poser.Advanced.ScaleSet", [this](LLUICtrl*, const LLSD&) { onAdvancedScaleSet(); });
@ -154,9 +158,7 @@ bool FSFloaterPoser::postBuild()
mToggleAdvancedPanelBtn = getChild<LLButton>("toggleAdvancedPanel"); mToggleAdvancedPanelBtn = getChild<LLButton>("toggleAdvancedPanel");
if (gSavedSettings.getBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY)) if (gSavedSettings.getBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY))
{
mToggleAdvancedPanelBtn->setValue(true); mToggleAdvancedPanelBtn->setValue(true);
}
mTrackpadSensitivitySlider = getChild<LLSliderCtrl>("trackpad_sensitivity_slider"); mTrackpadSensitivitySlider = getChild<LLSliderCtrl>("trackpad_sensitivity_slider");
mTrackpadSensitivitySlider->setValue(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY)); mTrackpadSensitivitySlider->setValue(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY));
@ -195,6 +197,9 @@ bool FSFloaterPoser::postBuild()
mToggleSympatheticRotationBtn = getChild<LLButton>("button_toggleSympatheticRotation"); mToggleSympatheticRotationBtn = getChild<LLButton>("button_toggleSympatheticRotation");
mToggleDeltaModeBtn = getChild<LLButton>("delta_mode_toggle"); mToggleDeltaModeBtn = getChild<LLButton>("delta_mode_toggle");
mRedoChangeBtn = getChild<LLButton>("button_redo_change"); mRedoChangeBtn = getChild<LLButton>("button_redo_change");
mSetToTposeButton = getChild<LLButton>("set_t_pose_button");
mRecaptureJointsButton = getChild<LLButton>("button_RecaptureParts");
mRecaptureJointsButton->setEnabled(!mDisableRecaptureUntilStopPosing);
mJointsParentPnl = getChild<LLPanel>("joints_parent_panel"); mJointsParentPnl = getChild<LLPanel>("joints_parent_panel");
mAdvancedParentPnl = getChild<LLPanel>("advanced_parent_panel"); mAdvancedParentPnl = getChild<LLPanel>("advanced_parent_panel");
@ -207,6 +212,10 @@ bool FSFloaterPoser::postBuild()
mMiscJointsPnl = getChild<LLPanel>("misc_joints_panel"); mMiscJointsPnl = getChild<LLPanel>("misc_joints_panel");
mCollisionVolumesPnl = getChild<LLPanel>("collision_volumes_panel"); mCollisionVolumesPnl = getChild<LLPanel>("collision_volumes_panel");
mStopPosingWhenClosed = getChild<LLCheckBoxCtrl>("stop_posing_on_close_checkbox");
if (gSavedSettings.getBOOL(POSER_STOPPOSINGWHENCLOSED_SAVE_KEY))
mStopPosingWhenClosed->set(true);
return true; return true;
} }
@ -217,13 +226,21 @@ void FSFloaterPoser::onOpen(const LLSD& key)
onJointSelect(); onJointSelect();
onOpenSetAdvancedPanel(); onOpenSetAdvancedPanel();
refreshPoseScroll(mHandPresetsScrollList, POSE_PRESETS_HANDS_SUBDIRECTORY); refreshPoseScroll(mHandPresetsScrollList, POSE_PRESETS_HANDS_SUBDIRECTORY);
startPosingSelf();
LLFloater::onOpen(key); LLFloater::onOpen(key);
} }
void FSFloaterPoser::onClose(bool app_quitting) void FSFloaterPoser::onClose(bool app_quitting)
{ {
gSavedSettings.setBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY, mToggleAdvancedPanelBtn->getValue().asBoolean()); if (mToggleAdvancedPanelBtn)
gSavedSettings.setBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY, mToggleAdvancedPanelBtn->getValue().asBoolean());
if (mStopPosingWhenClosed)
gSavedSettings.setBOOL(POSER_STOPPOSINGWHENCLOSED_SAVE_KEY, mStopPosingWhenClosed->getValue());
if (gSavedSettings.getBOOL(POSER_STOPPOSINGWHENCLOSED_SAVE_KEY))
stopPosingSelf();
LLFloater::onClose(app_quitting); LLFloater::onClose(app_quitting);
} }
@ -331,26 +348,25 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
std::string fullSavePath = std::string fullSavePath =
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT); gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT);
bool fromZeroRot = mPoserAnimator.posingStartedFromZeroRotations(avatar);
LLSD record; LLSD record;
S32 version = 3; record["version"]["value"] = (S32)4;
record["version"]["value"] = version; record["startFromTeePose"]["value"] = fromZeroRot;
LLVector3 rotation, position, scale;
for (const FSPoserAnimator::FSPoserJoint& pj : mPoserAnimator.PoserJoints) for (const FSPoserAnimator::FSPoserJoint& pj : mPoserAnimator.PoserJoints)
{ {
std::string bone_name = pj.jointName(); std::string bone_name = pj.jointName();
if (!mPoserAnimator.tryGetJointSaveVectors(avatar, pj, &rotation, &position, &scale))
continue;
LLVector3 vec3 = mPoserAnimator.getJointRotation(avatar, pj, SWAP_NOTHING, NEGATE_NOTHING); record[bone_name] = pj.jointName();
record[bone_name]["enabled"] = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
record[bone_name] = pj.jointName(); record[bone_name]["rotation"] = rotation.getValue();
record[bone_name]["rotation"] = vec3.getValue(); record[bone_name]["position"] = position.getValue();
record[bone_name]["scale"] = scale.getValue();
vec3 = mPoserAnimator.getJointPosition(avatar, pj);
record[bone_name]["position"] = vec3.getValue();
vec3 = mPoserAnimator.getJointScale(avatar, pj);
record[bone_name]["scale"] = vec3.getValue();
record[bone_name]["enabled"] = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
} }
llofstream file; llofstream file;
@ -394,7 +410,7 @@ void FSFloaterPoser::onClickToggleSelectedBoneEnabled()
refreshRotationSliders(); refreshRotationSliders();
refreshTrackpadCursor(); refreshTrackpadCursor();
refreshTextEmbiggeningOnAllScrollLists(); refreshTextHighlightingOnAllScrollLists();
} }
void FSFloaterPoser::onClickFlipSelectedJoints() void FSFloaterPoser::onClickFlipSelectedJoints()
@ -458,6 +474,9 @@ void FSFloaterPoser::onClickFlipPose()
void FSFloaterPoser::onClickRecaptureSelectedBones() void FSFloaterPoser::onClickRecaptureSelectedBones()
{ {
if (mDisableRecaptureUntilStopPosing)
return;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.size() < 1)
return; return;
@ -476,7 +495,7 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
continue; continue;
LLVector3 newRotation = mPoserAnimator.getJointRotation(avatar, *item, getJointTranslation(item->jointName()), LLVector3 newRotation = mPoserAnimator.getJointRotation(avatar, *item, getJointTranslation(item->jointName()),
getJointNegation(item->jointName()), true); getJointNegation(item->jointName()), CURRENTROTATION);
LLVector3 newPosition = mPoserAnimator.getJointPosition(avatar, *item, true); LLVector3 newPosition = mPoserAnimator.getJointPosition(avatar, *item, true);
LLVector3 newScale = mPoserAnimator.getJointScale(avatar, *item, true); LLVector3 newScale = mPoserAnimator.getJointScale(avatar, *item, true);
@ -490,7 +509,7 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
refreshRotationSliders(); refreshRotationSliders();
refreshTrackpadCursor(); refreshTrackpadCursor();
refreshTextEmbiggeningOnAllScrollLists(); refreshTextHighlightingOnAllScrollLists();
} }
void FSFloaterPoser::onClickBrowsePoseCache() void FSFloaterPoser::onClickBrowsePoseCache()
@ -673,14 +692,16 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
loadMethod == ROT_POS_AND_SCALES; loadMethod == ROT_POS_AND_SCALES;
bool loadScales = loadMethod == SCALES || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROTATIONS_AND_SCALES || bool loadScales = loadMethod == SCALES || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROTATIONS_AND_SCALES ||
loadMethod == ROT_POS_AND_SCALES; loadMethod == ROT_POS_AND_SCALES;
bool loadHandsOnly = loadMethod == HAND_RIGHT || loadMethod == HAND_LEFT;
try try
{ {
LLSD pose; LLSD pose;
llifstream infile; llifstream infile;
LLVector3 vec3; LLVector3 vec3;
bool enabled; LLQuaternion quat;
bool enabled;
S32 version = 0;
bool startFromZeroRot = false;
infile.open(fullPath); infile.open(fullPath);
if (!infile.is_open()) if (!infile.is_open())
@ -697,40 +718,48 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr) for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr)
{ {
std::string const &name = itr->first; std::string const& name = itr->first;
LLSD const &control_map = itr->second; LLSD const& control_map = itr->second;
if (loadHandsOnly && name.find("Hand") == std::string::npos) if (name == "startFromTeePose")
continue; startFromZeroRot = control_map["value"].asBoolean();
if (name == "version")
version = (S32)control_map["value"].asInteger();
}
bool loadPositionsAndScalesAsDeltas = false;
if (version > 3)
loadPositionsAndScalesAsDeltas = true;
if (startFromZeroRot) // old save formats will always start from T-Pose, for better or worse.
mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar);
for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr)
{
std::string const& name = itr->first;
LLSD const& control_map = itr->second;
const FSPoserAnimator::FSPoserJoint *poserJoint = mPoserAnimator.getPoserJointByName(name); const FSPoserAnimator::FSPoserJoint *poserJoint = mPoserAnimator.getPoserJointByName(name);
if (!poserJoint) if (!poserJoint)
continue; continue;
if (loadHandsOnly && control_map.has("rotation"))
{
vec3.setValue(control_map["rotation"]);
mPoserAnimator.setJointRotation(avatar, poserJoint, vec3, NONE, SWAP_NOTHING, NEGATE_NOTHING);
continue;
}
if (loadRotations && control_map.has("rotation")) if (loadRotations && control_map.has("rotation"))
{ {
vec3.setValue(control_map["rotation"]); vec3.setValue(control_map["rotation"]);
mPoserAnimator.setJointRotation(avatar, poserJoint, vec3, NONE, SWAP_NOTHING, NEGATE_NOTHING); // If we keep defaults BD poses mostly load, except fingers mPoserAnimator.loadJointRotation(avatar, poserJoint, vec3);
} }
if (loadPositions && control_map.has("position")) if (loadPositions && control_map.has("position"))
{ {
vec3.setValue(control_map["position"]); vec3.setValue(control_map["position"]);
mPoserAnimator.setJointPosition(avatar, poserJoint, vec3, NONE); mPoserAnimator.loadJointPosition(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
} }
if (loadScales && control_map.has("scale")) if (loadScales && control_map.has("scale"))
{ {
vec3.setValue(control_map["scale"]); vec3.setValue(control_map["scale"]);
mPoserAnimator.setJointScale(avatar, poserJoint, vec3, NONE); mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
} }
if (control_map.has("enabled")) if (control_map.has("enabled"))
@ -749,6 +778,35 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
onJointSelect(); onJointSelect();
} }
void FSFloaterPoser::startPosingSelf()
{
setUiSelectedAvatar(gAgentAvatarp->getID());
LLVOAvatar* avatar = getAvatarByUuid(gAgentAvatarp->getID());
if (!avatar)
return;
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);
if (!arePosingSelected && couldAnimateAvatar(avatar))
mPoserAnimator.tryPosingAvatar(avatar);
onAvatarSelect();
}
void FSFloaterPoser::stopPosingSelf()
{
LLVOAvatar* avatar = getAvatarByUuid(gAgentAvatarp->getID());
if (!avatar)
return;
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);
if (!arePosingSelected)
return;
mPoserAnimator.stopPosingAvatar(avatar);
onAvatarSelect();
reEnableRecaptureIfAllowed();
}
void FSFloaterPoser::onPoseStartStop() void FSFloaterPoser::onPoseStartStop()
{ {
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
@ -759,6 +817,7 @@ void FSFloaterPoser::onPoseStartStop()
if (arePosingSelected) if (arePosingSelected)
{ {
mPoserAnimator.stopPosingAvatar(avatar); mPoserAnimator.stopPosingAvatar(avatar);
reEnableRecaptureIfAllowed();
} }
else else
{ {
@ -799,7 +858,6 @@ bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar *avatar) const
void FSFloaterPoser::poseControlsEnable(bool enable) void FSFloaterPoser::poseControlsEnable(bool enable)
{ {
mJointsParentPnl->setEnabled(enable);
mAdvancedParentPnl->setEnabled(enable); mAdvancedParentPnl->setEnabled(enable);
mTrackballPnl->setEnabled(enable); mTrackballPnl->setEnabled(enable);
mFlipPoseBtn->setEnabled(enable); mFlipPoseBtn->setEnabled(enable);
@ -1060,6 +1118,24 @@ void FSFloaterPoser::onUndoLastScale()
refreshAdvancedScaleSliders(); refreshAdvancedScaleSliders();
} }
void FSFloaterPoser::onSetAvatarToTpose()
{
auto timeIntervalSinceLastClick = std::chrono::system_clock::now() - mTimeLastClickedJointReset;
mTimeLastClickedJointReset = std::chrono::system_clock::now();
if (timeIntervalSinceLastClick > mDoubleClickInterval)
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
mRecaptureJointsButton->setEnabled(false);
mSavePosesBtn->setLabel("Save Pose");
mDisableRecaptureUntilStopPosing = true;
mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar);
}
void FSFloaterPoser::onResetPosition() void FSFloaterPoser::onResetPosition()
{ {
// This is a double-click function: it needs to run twice within some amount of time to complete. // This is a double-click function: it needs to run twice within some amount of time to complete.
@ -1342,6 +1418,23 @@ LLVOAvatar* FSFloaterPoser::getUiSelectedAvatar() const
return getAvatarByUuid(selectedAvatarId); return getAvatarByUuid(selectedAvatarId);
} }
void FSFloaterPoser::setUiSelectedAvatar(const LLUUID& avatarToSelect)
{
for (auto listItem : mAvatarSelectionScrollList->getAllData())
{
LLScrollListCell* cell = listItem->getColumn(COL_UUID);
if (!cell)
continue;
LLUUID avatarId = cell->getValue().asUUID();
if (avatarId != avatarToSelect)
continue;
listItem->setSelected(true);
break;
}
}
void FSFloaterPoser::setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName() void FSFloaterPoser::setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName()
{ {
LLScrollListItem* item = mAvatarSelectionScrollList->getFirstSelected(); LLScrollListItem* item = mAvatarSelectionScrollList->getFirstSelected();
@ -1665,7 +1758,7 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const
return rotation; return rotation;
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()), rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()),
getJointNegation(selectedJoints.front()->jointName())); getJointNegation(selectedJoints.front()->jointName()), TARGETROTATION);
return rotation; return rotation;
} }
@ -1781,8 +1874,9 @@ void FSFloaterPoser::onAvatarSelect()
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar); bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);
mStartStopPosingBtn->setValue(arePosingSelected); mStartStopPosingBtn->setValue(arePosingSelected);
mSetToTposeButton->setEnabled(arePosingSelected);
poseControlsEnable(arePosingSelected); poseControlsEnable(arePosingSelected);
refreshTextEmbiggeningOnAllScrollLists(); refreshTextHighlightingOnAllScrollLists();
onJointSelect(); onJointSelect();
setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName();
} }
@ -1794,7 +1888,7 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const
for (LLCharacter* character : LLCharacter::sInstances) for (LLCharacter* character : LLCharacter::sInstances)
{ {
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character); LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character);
if (!couldAnimateAvatar(avatar)) if (!havePermissionToAnimateAvatar(avatar))
continue; continue;
avatar_ids.emplace_back(character->getID()); avatar_ids.emplace_back(character->getID());
@ -1889,7 +1983,7 @@ void FSFloaterPoser::onAvatarsRefresh()
row["columns"][COL_UUID]["value"] = uuid; row["columns"][COL_UUID]["value"] = uuid;
row["columns"][COL_SAVE]["column"] = "saveFileName"; row["columns"][COL_SAVE]["column"] = "saveFileName";
row["columns"][COL_SAVE]["value"] = ""; row["columns"][COL_SAVE]["value"] = "";
LLScrollListItem* item = mAvatarSelectionScrollList->addElement(row); LLScrollListItem* item = mAvatarSelectionScrollList->addElement(row);
} }
// Add Animesh avatars // Add Animesh avatars
@ -1917,12 +2011,11 @@ void FSFloaterPoser::onAvatarsRefresh()
} }
mAvatarSelectionScrollList->updateLayout(); mAvatarSelectionScrollList->updateLayout();
refreshTextEmbiggeningOnAllScrollLists(); refreshTextHighlightingOnAllScrollLists();
} }
void FSFloaterPoser::refreshTextEmbiggeningOnAllScrollLists() void FSFloaterPoser::refreshTextHighlightingOnAllScrollLists()
{ {
// the avatars
for (auto listItem : mAvatarSelectionScrollList->getAllData()) for (auto listItem : mAvatarSelectionScrollList->getAllData())
{ {
LLScrollListCell* cell = listItem->getColumn(COL_UUID); LLScrollListCell* cell = listItem->getColumn(COL_UUID);
@ -1946,6 +2039,34 @@ void FSFloaterPoser::refreshTextEmbiggeningOnAllScrollLists()
addBoldToScrollList(mCollisionVolumesScrollList, avatar); addBoldToScrollList(mCollisionVolumesScrollList, avatar);
} }
void FSFloaterPoser::reEnableRecaptureIfAllowed()
{
if (posingAnyoneOnScrollList())
return;
mRecaptureJointsButton->setEnabled(true);
mSavePosesBtn->setLabel("Save Diff");
mDisableRecaptureUntilStopPosing = false;
}
bool FSFloaterPoser::posingAnyoneOnScrollList()
{
for (auto listItem : mAvatarSelectionScrollList->getAllData())
{
LLScrollListCell* cell = listItem->getColumn(COL_UUID);
if (!cell)
continue;
LLUUID selectedAvatarId = cell->getValue().asUUID();
LLVOAvatar* listAvatar = getAvatarByUuid(selectedAvatarId);
if (mPoserAnimator.isPosingAvatar(listAvatar))
return true;
}
return false;
}
void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar) void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar)
{ {
if (!avatar) if (!avatar)

View File

@ -81,7 +81,8 @@ class FSFloaterPoser : public LLFloater
void onOpen(const LLSD& key) override; void onOpen(const LLSD& key) override;
void onClose(bool app_quitting) override; void onClose(bool app_quitting) override;
static bool mDisableRecaptureUntilStopPosing;
/// <summary> /// <summary>
/// Refreshes the supplied pose list from the supplued subdirectory. /// Refreshes the supplied pose list from the supplued subdirectory.
/// </summary> /// </summary>
@ -136,6 +137,12 @@ class FSFloaterPoser : public LLFloater
/// <returns>The currently selected avatar or animesh.</returns> /// <returns>The currently selected avatar or animesh.</returns>
LLVOAvatar* getUiSelectedAvatar() const; LLVOAvatar* getUiSelectedAvatar() const;
/// <summary>
/// Sets the UI selection for avatar or animesh.
/// </summary>
/// <param name="avatarToSelect">The ID of the avatar to select, if found.</param>
void setUiSelectedAvatar(const LLUUID& avatarToSelect);
/// <summary> /// <summary>
/// Gets the current bone-deflection style: encapsulates 'anything else you want to do' while you're manipulating a joint. /// Gets the current bone-deflection style: encapsulates 'anything else you want to do' while you're manipulating a joint.
/// Such as: fiddle the opposite joint too. /// Such as: fiddle the opposite joint too.
@ -216,8 +223,11 @@ class FSFloaterPoser : public LLFloater
void onRedoLastScale(); void onRedoLastScale();
void onResetPosition(); void onResetPosition();
void onResetScale(); void onResetScale();
void onSetAvatarToTpose();
void enableOrDisableRedoButton(); void enableOrDisableRedoButton();
void onPoseStartStop(); void onPoseStartStop();
void startPosingSelf();
void stopPosingSelf();
void onLimbTrackballChanged(); void onLimbTrackballChanged();
void onLimbYawPitchRollChanged(); void onLimbYawPitchRollChanged();
void onAvatarPositionSet(); void onAvatarPositionSet();
@ -284,9 +294,19 @@ class FSFloaterPoser : public LLFloater
S32 getJointNegation(const std::string& jointName) const; S32 getJointNegation(const std::string& jointName) const;
/// <summary> /// <summary>
/// The smallest text embiggens the noble selection. /// Refreshes the text on all scroll lists based on their state.
/// </summary> /// </summary>
void refreshTextEmbiggeningOnAllScrollLists(); void refreshTextHighlightingOnAllScrollLists();
/// <summary>
/// Recapture is be disabled if user is making their own pose (starting from a T-Pose).
/// </summary>
void reEnableRecaptureIfAllowed();
/// <summary>
/// Gets whether any avatar know by the UI is being posed.
/// </summary>
bool posingAnyoneOnScrollList();
/// <summary> /// <summary>
/// Applies the appropriate font-face (such as bold) to the text of the supplied list, to indicate use. /// Applies the appropriate font-face (such as bold) to the text of the supplied list, to indicate use.
@ -356,8 +376,10 @@ class FSFloaterPoser : public LLFloater
LLButton* mToggleSympatheticRotationBtn{ nullptr }; LLButton* mToggleSympatheticRotationBtn{ nullptr };
LLButton* mToggleDeltaModeBtn{ nullptr }; LLButton* mToggleDeltaModeBtn{ nullptr };
LLButton* mRedoChangeBtn{ nullptr }; LLButton* mRedoChangeBtn{ nullptr };
LLButton* mSetToTposeButton{ nullptr };
LLButton* mRecaptureJointsButton{ nullptr };
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr }; LLCheckBoxCtrl* mStopPosingWhenClosed{ nullptr };
LLLineEditor* mPoseSaveNameEditor{ nullptr }; LLLineEditor* mPoseSaveNameEditor{ nullptr };
LLPanel* mAdvancedParentPnl{ nullptr }; LLPanel* mAdvancedParentPnl{ nullptr };

View File

@ -412,7 +412,35 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
} }
} }
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, bool forRecapture) const bool FSPoserAnimator::posingStartedFromZeroRotations(LLVOAvatar* avatar) const
{
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
bool allStartingRotationsAreZero = posingMotion->allStartingRotationsAreZero();
if (allStartingRotationsAreZero)
return true;
return false;
}
void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
posingMotion->setAllRotationsToZero();
}
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const
{ {
LLVector3 vec3; LLVector3 vec3;
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
@ -427,11 +455,18 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi
return vec3; return vec3;
LLQuaternion rot; LLQuaternion rot;
if (forRecapture) switch (rotType)
rot = jointPose->getCurrentRotation(); {
else case TARGETROTATION:
rot = jointPose->getTargetRotation(); rot = jointPose->getTargetRotation();
break;
case CURRENTROTATION:
default:
rot = jointPose->getCurrentRotation();
break;
}
return translateRotationFromQuaternion(translation, negation, rot); return translateRotationFromQuaternion(translation, negation, rot);
} }
@ -709,6 +744,85 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
oppositeJointPose->setTargetScale(scale); oppositeJointPose->setTargetScale(scale);
} }
bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale)
{
if (!rot || !pos || !scale)
return false;
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
LLQuaternion difference = jointPose->getTargetRotation() * jointPose->getBeginningRotation().conjugate(); // diff * q1 = q2 -> diff = q2 * inverse(q1)
difference.getEulerAngles(&rot->mV[VX], &rot->mV[VY], &rot->mV[VZ]);
pos->set(jointPose->getTargetPosition() - jointPose->getBeginningPosition());
scale->set(jointPose->getTargetScale() - jointPose->getBeginningScale());
return true;
}
void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation)
{
if (!isAvatarSafeToUse(avatar) || !joint)
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setTargetRotation(rot * jointPose->getBeginningRotation());
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
{
if (!isAvatarSafeToUse(avatar) || !joint)
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (loadPositionAsDelta)
jointPose->setTargetPosition(jointPose->getBeginningPosition() + position);
else
jointPose->setTargetPosition(position);
}
void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale)
{
if (!isAvatarSafeToUse(avatar) || !joint)
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (loadScaleAsDelta)
jointPose->setTargetScale(jointPose->getTargetScale() + scale);
else
jointPose->setTargetScale(scale);
}
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName)
{ {
for (size_t index = 0; index != PoserJoints.size(); ++index) for (size_t index = 0; index != PoserJoints.size(); ++index)
@ -720,7 +834,7 @@ const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const
return nullptr; return nullptr;
} }
bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar *avatar) bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return false; return false;
@ -781,20 +895,18 @@ FSPosingMotion* FSPoserAnimator::getPosingMotion(LLVOAvatar* avatar) const
FSPosingMotion* FSPoserAnimator::findOrCreatePosingMotion(LLVOAvatar* avatar) FSPosingMotion* FSPoserAnimator::findOrCreatePosingMotion(LLVOAvatar* avatar)
{ {
FSPosingMotion* motion = getPosingMotion(avatar); FSPosingMotion* motion = getPosingMotion(avatar);
if (motion)
return motion;
if (!motion) LLTransactionID mTransactionID;
{ mTransactionID.generate();
LLTransactionID mTransactionID; LLAssetID animationAssetId = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
mTransactionID.generate();
LLAssetID animationAssetId = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
if (avatar->registerMotion(animationAssetId, FSPosingMotion::create)) if (avatar->registerMotion(animationAssetId, FSPosingMotion::create))
sAvatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId; sAvatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId;
return dynamic_cast<FSPosingMotion*>(avatar->createMotion(animationAssetId)); return dynamic_cast<FSPosingMotion*>(avatar->createMotion(animationAssetId));
}
return motion;
} }
bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const

View File

@ -56,6 +56,15 @@ typedef enum E_BoneDeflectionStyles
DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current
} E_BoneDeflectionStyles; } E_BoneDeflectionStyles;
/// <summary>
/// When getting the rotation of a joint, we can apply different considerations to the rotation.
/// </summary>
typedef enum E_BoneRotationType
{
CURRENTROTATION = 0, // the current rotation the joint has
TARGETROTATION = 1, // the rotation the we want to achieve
} E_BoneRotationType;
/// <summary> /// <summary>
/// When we're going from bone-rotation to the UI sliders, some of the axes need swapping so they make sense in UI-terms. /// When we're going from bone-rotation to the UI sliders, some of the axes need swapping so they make sense in UI-terms.
/// eg: for one bone, the X-axis may mean up and down, but for another bone, the x-axis might be left-right. /// eg: for one bone, the X-axis may mean up and down, but for another bone, the x-axis might be left-right.
@ -435,10 +444,9 @@ public:
/// <param name="joint">The joint to determine the rotation for.</param> /// <param name="joint">The joint to determine the rotation for.</param>
/// <param name="translation">The joint to determine the rotation for.</param> /// <param name="translation">The joint to determine the rotation for.</param>
/// <param name="negation">The style of negation to apply to the set.</param> /// <param name="negation">The style of negation to apply to the set.</param>
/// <param name="forRecapture">Get the current non-poser rotation, for recapture opportunity.</param> /// <param name="rotType">The type of rotation to get from the supplied joint for the supplied avatar.</param>
/// <returns>The rotation of the requested joint, if determinable, otherwise a default vector.</returns> /// <returns>The rotation of the requested joint, if determinable, otherwise a default vector.</returns>
LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const;
bool forRecapture = false) const;
/// <summary> /// <summary>
/// Sets the rotation of a joint for the supplied avatar. /// Sets the rotation of a joint for the supplied avatar.
@ -481,6 +489,83 @@ public:
/// <param name="avatar">The avatar whose pose should flip left-right.</param> /// <param name="avatar">The avatar whose pose should flip left-right.</param>
void flipEntirePose(LLVOAvatar* avatar); void flipEntirePose(LLVOAvatar* avatar);
/// <summary>
/// Sets all of the joint rotations of the supplied avatar to zero.
/// </summary>
/// <param name="avatar">The avatar whose joint rotations should be set to zero.</param>
void setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar);
/// <summary>
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
/// </summary>
/// <param name="avatar">The avatar whose pose-rotations are being considered for saving.</param>
/// <returns>True if the save should save only 'deltas' to the rotation, otherwise false.</returns>
/// <remarks>
/// A save of the rotation 'deltas' facilitates a user saving their changes to an existing animation.
/// Thus the save represents 'nothing other than the changes the user made', to some other pose which they may have limited rights to.
/// </remarks>
bool posingStartedFromZeroRotations(LLVOAvatar* avatar) const;
/// <summary>
/// Tries to get the rotation, position and scale changes from initial conditions, to save in some export container.
/// </summary>
/// <param name="avatar">The avatar whose pose is being considered for saving.</param>
/// <param name="joint">The joint we are considering the save for.</param>
/// <param name="rot">The quaternion to store the rotation to save in.</param>
/// <param name="pos">The vector to store the position to save in.</param>
/// <param name="scale">The vector to store the scale to save in.</param>
/// <returns>True if the joint should be saved, otherwise false.</returns>
/// <remarks>
/// Our objective is to protect peoples novel work: the poses created with this, and poses from other sources, such as in-world.
/// In all scenarios, this yeilds 'deltas' of rotation/position/scale.
/// The deltas represent the user's novel work, and may be relative to some initial values (as from a pose), or to 'nothing' (such as all rotations == 0, or, the 'T-Pose').
/// </remarks>
bool tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale);
/// <summary>
/// Loads a joint rotation for the supplied joint on the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to load the rotation for.</param>
/// <param name="joint">The joint to load the rotation for.</param>
/// <param name="rotation">The rotation to load.</param>
/// <remarks>
/// All rotations we load are deltas to the current rotation the supplied joint has.
/// Whether the joint already has a rotation because some animation is playing (sp possibly a non-zero rotation),
/// or whether it is a rotation relative to zero, the result is always the same: just 'add' this rotation to the existing.
/// </remarks>
void loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation);
/// <summary>
/// Loads a joint position for the supplied joint on the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to load the position for.</param>
/// <param name="joint">The joint to load the position for.</param>
/// <param name="loadPositionAsDelta">Whether to the supplied position as a delta to the current position, or not.</param>
/// <param name="position">The Position to apply to the supplied joint.</param>
/// <remarks>
/// A position is saved as an absolute if the user created the pose from 'scratch' (at present the 'T-Pose').
/// Otherwise the position is saved as a delta.
/// The primary purpose is aesthetic: the numbers inside of a 'delta save file' have 'zeros everywhere'.
/// A delta-save thus accurately reflects what the user changed, and not what the original creator of the modified pose specified.
/// 'Legacy' (pre save format version-4) poses we expect to load as absolutes.
/// </remarks>
void loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position);
/// <summary>
/// Loads a joint scale for the supplied joint on the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to load the scale for.</param>
/// <param name="joint">The joint to load the scale for.</param>
/// <param name="loadScaleAsDelta">Whether to the supplied scale as a delta to the current scale, or not.</param>
/// <param name="scale">The scale to apply to the supplied joint.</param>
/// <remarks>
/// A scale is saved as an absolute if the user created the pose from 'scratch' (at present the 'T-Pose').
/// Otherwise the scale is saved as a delta.
/// The primary purpose is somewhat aesthetic: the numbers inside of a 'pose modification XML' has zeros everywhere.
/// A delta-save thus accurately reflects what the user changed, and not what the original creator of the modified pose specified.
/// </remarks>
void loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale);
private: private:
/// <summary> /// <summary>
/// Translates a rotation vector from the UI to a Quaternion for the bone. /// Translates a rotation vector from the UI to a Quaternion for the bone.

View File

@ -249,6 +249,34 @@ bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint)
return (state & POSER_JOINT_STATE); return (state & POSER_JOINT_STATE);
} }
bool FSPosingMotion::allStartingRotationsAreZero() const
{
LLQuaternion zeroQuat;
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
if (poserJoint_iter->jointName() == "mPelvis")
continue;
if (poserJoint_iter->isCollisionVolume())
continue;
LLQuaternion quat = poserJoint_iter->getBeginningRotation();
if (quat != zeroQuat)
return false;
}
return true;
}
void FSPosingMotion::setAllRotationsToZero()
{
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
if (poserJoint_iter->isCollisionVolume())
continue;
poserJoint_iter->setRotationsToZero();
}
}
constexpr size_t MaximumUndoQueueLength = 20; constexpr size_t MaximumUndoQueueLength = 20;
@ -489,6 +517,8 @@ void FSPosingMotion::FSJointPose::revertCollisionVolume()
joint->setScale(mBeginningScale); joint->setScale(mBeginningScale);
} }
void FSPosingMotion::FSJointPose::setRotationsToZero() { mBeginningRotation = mTargetRotation = LLQuaternion(); }
FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume) FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume)
{ {
mJointState = new LLJointState; mJointState = new LLJointState;

View File

@ -222,6 +222,11 @@ public:
/// </summary> /// </summary>
void revertCollisionVolume(); void revertCollisionVolume();
/// <summary>
/// Sets all rotations to zero.
/// </summary>
void setRotationsToZero();
/// <summary> /// <summary>
/// Gets the pointer to the jointstate for the joint this represents. /// Gets the pointer to the jointstate for the joint this represents.
/// </summary> /// </summary>
@ -295,6 +300,17 @@ public:
/// <returns>The unique, per-session, per-character motion identity.</returns> /// <returns>The unique, per-session, per-character motion identity.</returns>
LLAssetID motionId() const { return mMotionID; } LLAssetID motionId() const { return mMotionID; }
/// <summary>
/// Gets whether all starting rotations are zero.
/// </summary>
/// <returns>True if all starting rotations are zero, otherwise false.</returns>
bool allStartingRotationsAreZero() const;
/// <summary>
/// Sets all of the non-Collision Volume rotations to zero.
/// </summary>
void setAllRotationsToZero();
private: private:
/// <summary> /// <summary>
/// The kind of joint state this animation is concerned with changing. /// The kind of joint state this animation is concerned with changing.

View File

@ -5,7 +5,7 @@ height="310"
layout="topleft" layout="topleft"
name="floater_poser" name="floater_poser"
title="Avatar and Animesh Poser" title="Avatar and Animesh Poser"
width="565"> width="403">
<string name="icon_category" translate="false">Inv_BodyShape</string> <string name="icon_category" translate="false">Inv_BodyShape</string>
<string name="icon_bone" translate="false"></string> <string name="icon_bone" translate="false"></string>
<string name="icon_object" translate="false">Inv_Object</string> <string name="icon_object" translate="false">Inv_Object</string>
@ -256,7 +256,7 @@ width="565">
follows="top|left|right" follows="top|left|right"
height="425" height="425"
layout="topleft" layout="topleft"
left="7" left="3"
border_size="-4" border_size="-4"
close_time_constant="0.02" close_time_constant="0.02"
open_time_constant="0.02" open_time_constant="0.02"
@ -273,68 +273,16 @@ width="565">
name="regular_controls_layout" name="regular_controls_layout"
height="290" height="290"
width="607"> width="607">
<panel
follows="left|top|bottom"
height="302"
background_visible="false"
layout="topleft"
visible="true"
mouse_opaque="false"
name="avatarSelection_panel"
top="0"
left="0"
width="153">
<avatar_list
allow_select="true"
follows="all"
height="383"
layout="topleft"
multi_select="true"
name="avatars_online"
keep_one_selected="false"
show_permissions_granted="true"
width="317" />
<scroll_list
column_padding="0"
draw_heading="true"
height="285"
can_sort="false"
follows="all"
layout="topleft"
left="1"
tool_tip="Select the Avatar or Animesh you want to animate."
width="153"
multi_select="false"
name="avatarSelection_scroll"
top="0">
<scroll_list.columns
label=""
name="icon"
relative_width="0.0" />
<scroll_list.columns
label="Select Someone..."
name="name"
relative_width="1.0" />
<scroll_list.columns
label="UUID"
name="uuid"
relative_width="0.0" />
<scroll_list.columns
label="SaveFileName"
name="saveFileName"
relative_width="0.0" />
</scroll_list>
</panel>
<panel <panel
follows="left" follows="left"
height="290" height="290"
background_visible="false" background_visible="false"
layout="topleft" layout="topleft"
enabled="false" enabled="true"
mouse_opaque="false" mouse_opaque="false"
name="joints_parent_panel" name="joints_parent_panel"
top="0" top="0"
left_pad="2" left="0"
width="235"> width="235">
<tab_container <tab_container
follows="all" follows="all"
@ -345,7 +293,7 @@ width="565">
enabled="true" enabled="true"
name="joints_tabs" name="joints_tabs"
tab_height="20" tab_height="20"
tab_width="50" tab_width="55"
tab_group="1" tab_group="1"
tab_position="left" tab_position="left"
top="0" top="0"
@ -545,6 +493,38 @@ width="565">
tab_position="top" tab_position="top"
top="0" top="0"
width="235"> width="235">
<panel
follows="all"
background_visible="false"
height="299"
layout="topleft"
left="0"
title="Adjust"
name="hands_joints_panel"
top="0"
width="481">
<scroll_list
column_padding="2"
draw_heading="true"
height="300"
follows="all"
can_sort="false"
layout="topleft"
left="2"
width="479"
multi_select="true"
name="hand_joints_scroll"
top="0">
<scroll_list.columns
label=""
name="icon"
relative_width="0.1" />
<scroll_list.columns
label="Body Part"
name="joint"
relative_width="0.9" />
</scroll_list>
</panel>
<panel <panel
follows="all" follows="all"
background_visible="false" background_visible="false"
@ -600,38 +580,6 @@ width="565">
function="Poser.LoadRightHand"/> function="Poser.LoadRightHand"/>
</button> </button>
</panel> </panel>
<panel
follows="all"
background_visible="false"
height="299"
layout="topleft"
left="0"
title="Adjust"
name="hands_joints_panel"
top="0"
width="481">
<scroll_list
column_padding="2"
draw_heading="true"
height="300"
follows="all"
can_sort="false"
layout="topleft"
left="2"
width="479"
multi_select="true"
name="hand_joints_scroll"
top="0">
<scroll_list.columns
label=""
name="icon"
relative_width="0.1" />
<scroll_list.columns
label="Body Part"
name="joint"
relative_width="0.9" />
</scroll_list>
</panel>
</tab_container> </tab_container>
<panel <panel
follows="all" follows="all"
@ -697,6 +645,157 @@ width="565">
relative_width="0.9" /> relative_width="0.9" />
</scroll_list> </scroll_list>
</panel> </panel>
<panel
follows="left|top|bottom"
height="302"
background_visible="false"
layout="topleft"
visible="true"
title="Posee"
mouse_opaque="false"
name="avatarSelection_panel"
top="0"
left="0"
width="153">
<avatar_list
allow_select="true"
follows="all"
height="150"
layout="topleft"
multi_select="true"
name="avatars_online"
keep_one_selected="false"
show_permissions_granted="true"
width="317" />
<scroll_list
column_padding="0"
draw_heading="true"
height="160"
can_sort="false"
follows="all"
layout="topleft"
left="3"
tool_tip="Select the Avatar or Animesh you want to animate."
width="151"
multi_select="false"
name="avatarSelection_scroll"
top="0">
<scroll_list.columns
label=""
name="icon"
relative_width="0.0" />
<scroll_list.columns
label="Select Someone to Pose..."
name="name"
relative_width="1.0" />
<scroll_list.columns
label="UUID"
name="uuid"
relative_width="0.0" />
<scroll_list.columns
label="SaveFileName"
name="saveFileName"
relative_width="0.0" />
</scroll_list>
<button
height="21"
follows="top|left"
layout="topleft"
label=""
image_overlay="Refresh_Off"
image_unselected="Toolbar_Middle_Off"
name="refresh_avatars"
tool_tip="Refresh the list of avatars and animeshes"
width="20"
top="142"
left="3">
<button.commit_callback
function="Poser.RefreshAvatars"/>
</button>
<button
height="21"
follows="top|left"
enabled="false"
is_toggle="true"
label="Start Posing"
top_delta="0"
left_pad="2"
label_selected="Stop Posing"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
tool_tip="Start posing the selected avatar or animesh, if you are allowed to"
name="start_stop_posing_button"
width="150">
<button.commit_callback
function="Poser.StartStopAnimating"/>
</button>
<button
height="21"
follows="top|left"
layout="topleft"
left="3"
top_pad="1"
label="Set to T-Pose"
image_unselected="Toolbar_Middle_Off"
name="set_t_pose_button"
tool_tip="Double-click to set the selected avatar to a 'T-Pose'"
width="172">
<button.commit_callback
function="Poser.SetToTPose"/>
</button>
</panel>
<panel
follows="all"
background_visible="false"
height="299"
layout="topleft"
left="0"
title="Settings"
name="settings_panel"
top="0"
width="481">
<text follows="left|top"
name="limb_pitch_label"
height="10"
layout="topleft"
left_delta="3"
top_pad="2"
width="200">Trackpad Sensitivity:</text>
<slider
decimal_digits="2"
can_edit_text="true"
follows="left|top"
height="14"
increment="0.01"
initial_value="0"
layout="topleft"
left_delta="0"
max_val="2"
min_val="0.01"
name="trackpad_sensitivity_slider"
tool_tip="Adjusts the sensitivity of the trackball"
top_pad="3"
logarithmic="1"
width="170" >
<slider.commit_callback
function="Poser.AdjustTrackPadSensitivity"
parameter="2"/>
</slider>
<check_box
name="stop_posing_on_close_checkbox"
height="16"
label="Stop posing when closed"
follows="left|top"
left="5"
tool_tip="Not stopping you pose can be helpful if you do a lot of work, and don't want to accidentally lose it."
top_pad="5"
width="134" />
</panel>
</tab_container> </tab_container>
</panel> </panel>
<panel <panel
@ -961,43 +1060,6 @@ width="565">
left="0" left="0"
name="button_controls_panel" name="button_controls_panel"
width="800"> width="800">
<button
height="21"
follows="top|left"
layout="topleft"
label=""
image_overlay="Refresh_Off"
image_unselected="Toolbar_Middle_Off"
name="refresh_avatars"
tool_tip="Refresh the list of avatars and animeshes"
width="20"
top_pad="-1"
left="2">
<button.commit_callback
function="Poser.RefreshAvatars"/>
</button>
<button
height="21"
follows="top|left"
enabled="false"
is_toggle="true"
label="Start Posing"
top_delta="0"
left_pad="2"
label_selected="Stop Posing"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
tool_tip="Start posing the selected avatar or animesh, if you are allowed to"
name="start_stop_posing_button"
width="108">
<button.commit_callback
function="Poser.StartStopAnimating"/>
</button>
<button <button
height="21" height="21"
follows="top|left" follows="top|left"
@ -1012,7 +1074,8 @@ width="565">
name="toggleAdvancedPanel" name="toggleAdvancedPanel"
tool_tip="Toggle the Advanced Settings area" tool_tip="Toggle the Advanced Settings area"
width="20" width="20"
left_pad="2"> top_pad="-1"
left="2">
<button.commit_callback <button.commit_callback
function="Poser.ToggleAdvancedPanel"/> function="Poser.ToggleAdvancedPanel"/>
</button> </button>
@ -1025,8 +1088,8 @@ width="565">
mouse_opaque="false" mouse_opaque="false"
left_pad="0" left_pad="0"
top_delta="0" top_delta="0"
name="advbutton_spacer_panel" name="button_spacer_panel"
width="54"/> width="36"/>
<button <button
height="21" height="21"
follows="top|left" follows="top|left"
@ -1037,7 +1100,7 @@ width="565">
image_unselected="Toolbar_Middle_Off" image_unselected="Toolbar_Middle_Off"
name="FlipPose_avatar" name="FlipPose_avatar"
tool_tip="Flip the whole pose left/right" tool_tip="Flip the whole pose left/right"
width="23" width="21"
top_delta="0" top_delta="0"
left_pad="1"> left_pad="1">
<button.commit_callback <button.commit_callback
@ -1049,11 +1112,11 @@ width="565">
layout="topleft" layout="topleft"
enabled="false" enabled="false"
label="" label=""
image_overlay="Sync_Progress_1" image_overlay="Edit_Flip_X"
image_unselected="Toolbar_Middle_Off" image_unselected="Toolbar_Middle_Off"
name="FlipJoint_avatar" name="FlipJoint_avatar"
tool_tip="Mirror the selected body part(s) left/right" tool_tip="Mirror the selected body part(s) left/right"
width="23" width="21"
top_delta="0" top_delta="0"
left_pad="1"> left_pad="1">
<button.commit_callback <button.commit_callback
@ -1089,7 +1152,7 @@ width="565">
left_pad="1" left_pad="1"
top_delta="0" top_delta="0"
tool_tip="Turn the Poser on or off for the selected body part(s). When off, that body part animates like normal (with your AO or a pose-ball/etc.)" tool_tip="Turn the Poser on or off for the selected body part(s). When off, that body part animates like normal (with your AO or a pose-ball/etc.)"
width="85" > width="84" >
<button.commit_callback <button.commit_callback
function="Poser.TogglePosingSelectedBones"/> function="Poser.TogglePosingSelectedBones"/>
</button> </button>
@ -1103,7 +1166,7 @@ width="565">
left_pad="0" left_pad="0"
top_delta="0" top_delta="0"
name="button_spacer_panel" name="button_spacer_panel"
width="60"/> width="58"/>
<button <button
follows="left|top" follows="left|top"
height="21" height="21"
@ -1172,10 +1235,10 @@ width="565">
height="21" height="21"
follows="top|left" follows="top|left"
layout="topleft" layout="topleft"
label="Save Pose" label="Save Diff"
enabled="false" enabled="false"
visible="false" visible="false"
tool_tip="Save the current pose." tool_tip="A Diff is just your changes to an existing Pose. Start from a T-Pose to save a new work"
image_overlay="Icon_Dock_Foreground" image_overlay="Icon_Dock_Foreground"
image_overlay_alignment="left" image_overlay_alignment="left"
image_hover_unselected="Toolbar_Middle_Over" image_hover_unselected="Toolbar_Middle_Over"
@ -1246,7 +1309,7 @@ width="565">
min_val="-0.5" min_val="-0.5"
name="Advanced_Position_X" name="Advanced_Position_X"
top_pad="6" top_pad="6"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.PositionSet" function="Poser.Advanced.PositionSet"
parameter="0"/> parameter="0"/>
@ -1266,7 +1329,7 @@ width="565">
min_val="-0.5" min_val="-0.5"
name="Advanced_Position_Y" name="Advanced_Position_Y"
top_pad="1" top_pad="1"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.PositionSet" function="Poser.Advanced.PositionSet"
parameter="1"/> parameter="1"/>
@ -1286,7 +1349,7 @@ width="565">
min_val="-0.5" min_val="-0.5"
name="Advanced_Position_Z" name="Advanced_Position_Z"
top_pad="1" top_pad="1"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.PositionSet" function="Poser.Advanced.PositionSet"
parameter="2"/> parameter="2"/>
@ -1331,7 +1394,7 @@ width="565">
image_overlay="Inv_TrashOpen" image_overlay="Inv_TrashOpen"
image_overlay_alignment="left" image_overlay_alignment="left"
image_unselected="Toolbar_Middle_Off" image_unselected="Toolbar_Middle_Off"
name="reset_positions" name="undo_position_change"
tool_tip="Double click to reset position back to original" tool_tip="Double click to reset position back to original"
width="110" width="110"
top_delta="0" top_delta="0"
@ -1365,7 +1428,7 @@ width="565">
min_val="0" min_val="0"
name="Advanced_Scale_X" name="Advanced_Scale_X"
top_pad="6" top_pad="6"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.ScaleSet" function="Poser.Advanced.ScaleSet"
parameter="0"/> parameter="0"/>
@ -1385,7 +1448,7 @@ width="565">
min_val="0" min_val="0"
name="Advanced_Scale_Y" name="Advanced_Scale_Y"
top_pad="1" top_pad="1"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.ScaleSet" function="Poser.Advanced.ScaleSet"
parameter="1"/> parameter="1"/>
@ -1405,7 +1468,7 @@ width="565">
min_val="0" min_val="0"
name="Advanced_Scale_Z" name="Advanced_Scale_Z"
top_pad="1" top_pad="1"
width="530" > width="380" >
<slider.commit_callback <slider.commit_callback
function="Poser.Advanced.ScaleSet" function="Poser.Advanced.ScaleSet"
parameter="2"/> parameter="2"/>
@ -1418,7 +1481,7 @@ width="565">
image_overlay="Script_Undo" image_overlay="Script_Undo"
image_overlay_alignment="left" image_overlay_alignment="left"
image_unselected="Toolbar_Middle_Off" image_unselected="Toolbar_Middle_Off"
name="undo_scale_change" name="undo_position_change"
tool_tip="Undo the last scale change" tool_tip="Undo the last scale change"
width="150" width="150"
top_pad="2" top_pad="2"
@ -1450,7 +1513,7 @@ width="565">
image_overlay="Inv_TrashOpen" image_overlay="Inv_TrashOpen"
image_overlay_alignment="left" image_overlay_alignment="left"
image_unselected="Toolbar_Middle_Off" image_unselected="Toolbar_Middle_Off"
name="reset_scales" name="undo_scale_change"
tool_tip="Double click to reset scale back to original" tool_tip="Double click to reset scale back to original"
width="110" width="110"
top_delta="0" top_delta="0"
@ -1461,39 +1524,6 @@ width="565">
</panel> </panel>
</tab_container> </tab_container>
</panel> </panel>
<panel
follows="left|top"
height="100"
background_visible="false"
layout="topleft"
visible="false"
enabled="true"
mouse_opaque="false"
name="save_file_options"
left_pad="2"
width="235">
<slider
decimal_digits="2"
can_edit_text="true"
follows="left|top"
height="14"
increment="0.01"
initial_value="0"
label="Sensitivity"
label_width="70"
layout="topleft"
left_delta="0"
max_val="2"
min_val="0.0001"
name="trackpad_sensitivity_slider"
tool_tip="Adjusts the sensitivity of the trackball"
top_pad="1"
width="200" >
<slider.commit_callback
function="Poser.AdjustTrackPadSensitivity"
parameter="2"/>
</slider>
</panel>
</layout_panel> </layout_panel>
</layout_stack> </layout_stack>
</floater> </floater>