diff --git a/.github/workflows/tag-fs-build.yml b/.github/workflows/tag-fs-build.yml index e38c891ffc..574de07d2e 100644 --- a/.github/workflows/tag-fs-build.yml +++ b/.github/workflows/tag-fs-build.yml @@ -134,12 +134,11 @@ jobs: # Create the Tag (Conditional) - name: Create tag if: > - ${{ - steps.get_inputs.outputs.release_type != 'Nightly' && - steps.get_inputs.outputs.release_type != 'Manual' && - steps.get_inputs.outputs.release_type != 'Profiling' && - steps.get_inputs.outputs.dry_run != 'true' && - steps.check_tag.outputs.tag_exists == 'false' }} + steps.get_inputs.outputs.release_type != 'Nightly' && + steps.get_inputs.outputs.release_type != 'Manual' && + steps.get_inputs.outputs.release_type != 'Profiling' && + steps.get_inputs.outputs.dry_run != 'true' && + steps.check_tag.outputs.tag_exists == 'false' run: | echo "Creating tag: ${{ steps.get_tag.outputs.tag_name }}" git tag ${{ steps.get_tag.outputs.tag_name }} @@ -148,8 +147,8 @@ jobs: # Push the Tag to the Repository (Conditional) - name: Push tag if: > - ${{ steps.get_inputs.outputs.dry_run != 'true' && - steps.check_tag.outputs.tag_exists == 'false' }} + steps.get_inputs.outputs.dry_run != 'true' && + steps.check_tag.outputs.tag_exists == 'false' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/indra/llinventory/llsettingsbase.cpp b/indra/llinventory/llsettingsbase.cpp index 0ee71de3a1..2143ae54a0 100644 --- a/indra/llinventory/llsettingsbase.cpp +++ b/indra/llinventory/llsettingsbase.cpp @@ -764,7 +764,14 @@ void LLSettingsBlender::update(const LLSettingsBase::BlendFactor& blendf) F64 res = setBlendFactor(blendf); llassert(res >= 0.0 && res <= 1.0); (void)res; - mTarget->update(); + // FIRE-34805 another issue with missing EEP on or shortly after login. + // Ideally we'll find the true fix at a higher level. But for now fix the symptom. + // mTarget->update(); + if(mTarget) + { + mTarget->update(); + } + // } F64 LLSettingsBlender::setBlendFactor(const LLSettingsBase::BlendFactor& blendf_in) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 95794bff13..e394cc5784 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8067,6 +8067,17 @@ Value 0 + FSPoserStopPosingWhenClosed + + Comment + Whether to stop animating with the poser when the poser window is closed. + Persist + 1 + Type + Boolean + Value + 1 + FSPoserTrackpadSensitivity Comment diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 7b315f733d..508574d988 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -40,6 +40,7 @@ #include "llviewercontrol.h" #include "llviewerwindow.h" #include "llwindow.h" +#include "llvoavatarself.h" namespace { @@ -52,6 +53,7 @@ constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_"; constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_"; constexpr std::string_view POSER_ADVANCEDWINDOWSTATE_SAVE_KEY = "FSPoserAdvancedWindowState"; constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity"; +constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed"; } // namespace /// @@ -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. /// constexpr F32 NormalTrackpadRangeInRads = F_PI; +bool FSFloaterPoser::sDisableRecaptureUntilStopPosing; 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.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.ScaleSet", [this](LLUICtrl*, const LLSD&) { onAdvancedScaleSet(); }); @@ -154,9 +158,7 @@ bool FSFloaterPoser::postBuild() mToggleAdvancedPanelBtn = getChild("toggleAdvancedPanel"); if (gSavedSettings.getBOOL(POSER_ADVANCEDWINDOWSTATE_SAVE_KEY)) - { mToggleAdvancedPanelBtn->setValue(true); - } mTrackpadSensitivitySlider = getChild("trackpad_sensitivity_slider"); mTrackpadSensitivitySlider->setValue(gSavedSettings.getF32(POSER_TRACKPAD_SENSITIVITY_SAVE_KEY)); @@ -178,7 +180,6 @@ bool FSFloaterPoser::postBuild() mAdvScaleYSlider = getChild("Advanced_Scale_Y"); mAdvScaleZSlider = getChild("Advanced_Scale_Z"); - mSaveFilePptionsPnl = getChild("save_file_options"); mPosesLoadSavePnl = getChild("poses_loadSave"); mStartStopPosingBtn = getChild("start_stop_posing_button"); mToggleLoadSavePanelBtn = getChild("toggleLoadSavePanel"); @@ -195,6 +196,9 @@ bool FSFloaterPoser::postBuild() mToggleSympatheticRotationBtn = getChild("button_toggleSympatheticRotation"); mToggleDeltaModeBtn = getChild("delta_mode_toggle"); mRedoChangeBtn = getChild("button_redo_change"); + mSetToTposeButton = getChild("set_t_pose_button"); + mRecaptureJointsButton = getChild("button_RecaptureParts"); + mRecaptureJointsButton->setEnabled(!sDisableRecaptureUntilStopPosing); mJointsParentPnl = getChild("joints_parent_panel"); mAdvancedParentPnl = getChild("advanced_parent_panel"); @@ -217,13 +221,18 @@ void FSFloaterPoser::onOpen(const LLSD& key) onJointSelect(); onOpenSetAdvancedPanel(); refreshPoseScroll(mHandPresetsScrollList, POSE_PRESETS_HANDS_SUBDIRECTORY); + startPosingSelf(); LLFloater::onOpen(key); } 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 (gSavedSettings.getBOOL(POSER_STOPPOSINGWHENCLOSED_SAVE_KEY)) + stopPosingSelf(); LLFloater::onClose(app_quitting); } @@ -290,6 +299,12 @@ void FSFloaterPoser::onPoseFileSelect() LLStringExplicit name = LLStringExplicit(poseName); mPoseSaveNameEditor->setEnabled(enableButtons); mPoseSaveNameEditor->setText(name); + + bool isDeltaSave = !poseFileStartsFromTeePose(name); + if (isDeltaSave) + mLoadPosesBtn->setLabel("Load Diff"); + else + mLoadPosesBtn->setLabel("Load Pose"); } void FSFloaterPoser::onClickPoseSave() @@ -331,26 +346,30 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi std::string fullSavePath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT); + bool savingDiff = !mPoserAnimator.posingStartedFromZeroRotations(avatar); + LLSD record; - S32 version = 3; - record["version"]["value"] = version; + record["version"]["value"] = (S32)4; + record["startFromTeePose"]["value"] = !savingDiff; + + LLVector3 rotation, position, scale, zeroVector; 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); + bool jointRotPosScaleAllZero = rotation == zeroVector && position == zeroVector && scale == zeroVector; + bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj); + if (savingDiff && (!posingThisJoint || jointRotPosScaleAllZero)) + continue; - record[bone_name] = pj.jointName(); - record[bone_name]["rotation"] = vec3.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); + record[bone_name] = bone_name; + record[bone_name]["enabled"] = posingThisJoint; + record[bone_name]["rotation"] = rotation.getValue(); + record[bone_name]["position"] = position.getValue(); + record[bone_name]["scale"] = scale.getValue(); } llofstream file; @@ -394,7 +413,7 @@ void FSFloaterPoser::onClickToggleSelectedBoneEnabled() refreshRotationSliders(); refreshTrackpadCursor(); - refreshTextEmbiggeningOnAllScrollLists(); + refreshTextHighlightingOnAllScrollLists(); } void FSFloaterPoser::onClickFlipSelectedJoints() @@ -458,6 +477,9 @@ void FSFloaterPoser::onClickFlipPose() void FSFloaterPoser::onClickRecaptureSelectedBones() { + if (sDisableRecaptureUntilStopPosing) + return; + auto selectedJoints = getUiSelectedPoserJoints(); if (selectedJoints.size() < 1) return; @@ -475,22 +497,12 @@ void FSFloaterPoser::onClickRecaptureSelectedBones() if (currentlyPosing) continue; - LLVector3 newRotation = mPoserAnimator.getJointRotation(avatar, *item, getJointTranslation(item->jointName()), - getJointNegation(item->jointName()), true); - LLVector3 newPosition = mPoserAnimator.getJointPosition(avatar, *item, true); - LLVector3 newScale = mPoserAnimator.getJointScale(avatar, *item, true); - - mPoserAnimator.setPosingAvatarJoint(avatar, *item, true); - - mPoserAnimator.setJointRotation(avatar, item, newRotation, NONE, getJointTranslation(item->jointName()), - getJointNegation(item->jointName())); - mPoserAnimator.setJointPosition(avatar, item, newPosition, NONE); - mPoserAnimator.setJointScale(avatar, item, newScale, NONE); + mPoserAnimator.recaptureJoint(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName())); } refreshRotationSliders(); refreshTrackpadCursor(); - refreshTextEmbiggeningOnAllScrollLists(); + refreshTextHighlightingOnAllScrollLists(); } void FSFloaterPoser::onClickBrowsePoseCache() @@ -655,6 +667,51 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand) } +bool FSFloaterPoser::poseFileStartsFromTeePose(const std::string& poseFileName) +{ + std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); + if (!gDirUtilp->fileExists(pathname)) + return false; + + std::string fullPath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_INTERNAL_FORMAT_FILE_EXT); + + try + { + LLSD pose; + llifstream infile; + bool startFromZeroRot = false; + + infile.open(fullPath); + if (!infile.is_open()) + return false; + + S32 lineCount = LLSDSerialize::fromXML(pose, infile); + if (lineCount == LLSDParser::PARSE_FAILURE) + { + LL_WARNS("Posing") << "Failed to parse file: " << poseFileName << LL_ENDL; + return startFromZeroRot; + } + + for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr) + { + std::string const& name = itr->first; + LLSD const& control_map = itr->second; + + if (name == "startFromTeePose") + startFromZeroRot = control_map["value"].asBoolean(); + } + + return startFromZeroRot; + } + catch (const std::exception& e) + { + LL_WARNS("Posing") << "Unable to load or parse the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL; + } + + return false; +} + void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod) { std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY); @@ -673,14 +730,16 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose loadMethod == ROT_POS_AND_SCALES; bool loadScales = loadMethod == SCALES || loadMethod == POSITIONS_AND_SCALES || loadMethod == ROTATIONS_AND_SCALES || loadMethod == ROT_POS_AND_SCALES; - bool loadHandsOnly = loadMethod == HAND_RIGHT || loadMethod == HAND_LEFT; try { - LLSD pose; - llifstream infile; - LLVector3 vec3; - bool enabled; + LLSD pose; + llifstream infile; + LLVector3 vec3; + LLQuaternion quat; + bool enabled; + S32 version = 0; + bool startFromZeroRot = false; infile.open(fullPath); if (!infile.is_open()) @@ -697,40 +756,51 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr) { - std::string const &name = itr->first; - LLSD const &control_map = itr->second; + std::string const& name = itr->first; + LLSD const& control_map = itr->second; - if (loadHandsOnly && name.find("Hand") == std::string::npos) - continue; + if (name == "startFromTeePose") + startFromZeroRot = control_map["value"].asBoolean(); + + if (name == "version") + version = (S32)control_map["value"].asInteger(); + } + + bool loadPositionsAndScalesAsDeltas = false; + if (version > 3) + loadPositionsAndScalesAsDeltas = true; + + if (startFromZeroRot) // legacy saves will always start from T-Pose, for better or worse. + { + disableRecapture(); + 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); if (!poserJoint) 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")) { 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")) { vec3.setValue(control_map["position"]); - mPoserAnimator.setJointPosition(avatar, poserJoint, vec3, NONE); + mPoserAnimator.loadJointPosition(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3); } if (loadScales && control_map.has("scale")) { vec3.setValue(control_map["scale"]); - mPoserAnimator.setJointScale(avatar, poserJoint, vec3, NONE); + mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3); } if (control_map.has("enabled")) @@ -740,7 +810,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose } } } - } + } catch ( const std::exception & e ) { LL_WARNS("Posing") << "Everything caught fire trying to load the pose: " << poseFileName << " exception: " << e.what() << LL_ENDL; @@ -749,6 +819,35 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose 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() { LLVOAvatar* avatar = getUiSelectedAvatar(); @@ -759,6 +858,7 @@ void FSFloaterPoser::onPoseStartStop() if (arePosingSelected) { mPoserAnimator.stopPosingAvatar(avatar); + reEnableRecaptureIfAllowed(); } else { @@ -799,7 +899,6 @@ bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar *avatar) const void FSFloaterPoser::poseControlsEnable(bool enable) { - mJointsParentPnl->setEnabled(enable); mAdvancedParentPnl->setEnabled(enable); mTrackballPnl->setEnabled(enable); mFlipPoseBtn->setEnabled(enable); @@ -947,16 +1046,6 @@ void FSFloaterPoser::onToggleLoadSavePanel() if (loadSavePanelExpanded) refreshPoseScroll(mPosesScrollList); - - showOrHideAdvancedSaveOptions(); -} - -void FSFloaterPoser::showOrHideAdvancedSaveOptions() -{ - bool loadSavePanelExpanded = mToggleLoadSavePanelBtn->getValue().asBoolean(); - bool advancedPanelExpanded = mToggleAdvancedPanelBtn->getValue().asBoolean(); - - mSaveFilePptionsPnl->setVisible(loadSavePanelExpanded && advancedPanelExpanded); } void FSFloaterPoser::onToggleMirrorChange() @@ -1060,6 +1149,21 @@ void FSFloaterPoser::onUndoLastScale() 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; + + disableRecapture(); + mPoserAnimator.setAllAvatarStartingRotationsToZero(avatar); +} + void FSFloaterPoser::onResetPosition() { // This is a double-click function: it needs to run twice within some amount of time to complete. @@ -1242,7 +1346,6 @@ void FSFloaterPoser::onToggleAdvancedPanel() return; reshape(poserFloaterWidth, poserFloaterHeight); - showOrHideAdvancedSaveOptions(); onJointSelect(); } @@ -1342,6 +1445,23 @@ LLVOAvatar* FSFloaterPoser::getUiSelectedAvatar() const 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() { LLScrollListItem* item = mAvatarSelectionScrollList->getFirstSelected(); @@ -1665,7 +1785,7 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const return rotation; rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()), - getJointNegation(selectedJoints.front()->jointName())); + getJointNegation(selectedJoints.front()->jointName()), TARGETROTATION); return rotation; } @@ -1781,8 +1901,9 @@ void FSFloaterPoser::onAvatarSelect() bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar); mStartStopPosingBtn->setValue(arePosingSelected); + mSetToTposeButton->setEnabled(arePosingSelected); poseControlsEnable(arePosingSelected); - refreshTextEmbiggeningOnAllScrollLists(); + refreshTextHighlightingOnAllScrollLists(); onJointSelect(); setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); } @@ -1794,7 +1915,7 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const for (LLCharacter* character : LLCharacter::sInstances) { LLVOAvatar* avatar = dynamic_cast(character); - if (!couldAnimateAvatar(avatar)) + if (!havePermissionToAnimateAvatar(avatar)) continue; avatar_ids.emplace_back(character->getID()); @@ -1889,7 +2010,7 @@ void FSFloaterPoser::onAvatarsRefresh() row["columns"][COL_UUID]["value"] = uuid; row["columns"][COL_SAVE]["column"] = "saveFileName"; row["columns"][COL_SAVE]["value"] = ""; - LLScrollListItem* item = mAvatarSelectionScrollList->addElement(row); + LLScrollListItem* item = mAvatarSelectionScrollList->addElement(row); } // Add Animesh avatars @@ -1917,12 +2038,11 @@ void FSFloaterPoser::onAvatarsRefresh() } mAvatarSelectionScrollList->updateLayout(); - refreshTextEmbiggeningOnAllScrollLists(); + refreshTextHighlightingOnAllScrollLists(); } -void FSFloaterPoser::refreshTextEmbiggeningOnAllScrollLists() +void FSFloaterPoser::refreshTextHighlightingOnAllScrollLists() { - // the avatars for (auto listItem : mAvatarSelectionScrollList->getAllData()) { LLScrollListCell* cell = listItem->getColumn(COL_UUID); @@ -1946,6 +2066,41 @@ void FSFloaterPoser::refreshTextEmbiggeningOnAllScrollLists() addBoldToScrollList(mCollisionVolumesScrollList, avatar); } +void FSFloaterPoser::disableRecapture() +{ + mRecaptureJointsButton->setEnabled(false); + mSavePosesBtn->setLabel("Save Pose"); + sDisableRecaptureUntilStopPosing = true; +} + +void FSFloaterPoser::reEnableRecaptureIfAllowed() +{ + if (posingAnyoneOnScrollList()) + return; + + mRecaptureJointsButton->setEnabled(true); + mSavePosesBtn->setLabel("Save Diff"); + sDisableRecaptureUntilStopPosing = 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) { if (!avatar) diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index a2362ba6bc..7f8cfa2a93 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -81,7 +81,8 @@ class FSFloaterPoser : public LLFloater void onOpen(const LLSD& key) override; void onClose(bool app_quitting) override; - + static bool sDisableRecaptureUntilStopPosing; + /// /// Refreshes the supplied pose list from the supplued subdirectory. /// @@ -136,6 +137,12 @@ class FSFloaterPoser : public LLFloater /// The currently selected avatar or animesh. LLVOAvatar* getUiSelectedAvatar() const; + /// + /// Sets the UI selection for avatar or animesh. + /// + /// The ID of the avatar to select, if found. + void setUiSelectedAvatar(const LLUUID& avatarToSelect); + /// /// 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. @@ -195,9 +202,9 @@ class FSFloaterPoser : public LLFloater void onClickBrowsePoseCache(); void onPoseMenuAction(const LLSD& param); void loadPoseFromXml(LLVOAvatar* avatar, const std::string& poseFileName, E_LoadPoseMethods loadMethod); + bool poseFileStartsFromTeePose(const std::string& poseFileName); void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); void setUiSelectedAvatarSaveFileName(const std::string& saveFileName); - void showOrHideAdvancedSaveOptions(); // UI Event Handlers: void onAvatarsRefresh(); @@ -216,8 +223,11 @@ class FSFloaterPoser : public LLFloater void onRedoLastScale(); void onResetPosition(); void onResetScale(); + void onSetAvatarToTpose(); void enableOrDisableRedoButton(); void onPoseStartStop(); + void startPosingSelf(); + void stopPosingSelf(); void onLimbTrackballChanged(); void onLimbYawPitchRollChanged(); void onAvatarPositionSet(); @@ -284,9 +294,24 @@ class FSFloaterPoser : public LLFloater S32 getJointNegation(const std::string& jointName) const; /// - /// The smallest text embiggens the noble selection. + /// Refreshes the text on all scroll lists based on their state. /// - void refreshTextEmbiggeningOnAllScrollLists(); + void refreshTextHighlightingOnAllScrollLists(); + + /// + /// Disables recapturing joint traits. + /// + void disableRecapture(); + + /// + /// Recapture is be disabled if user is making their own pose (starting from a T-Pose). + /// + void reEnableRecaptureIfAllowed(); + + /// + /// Gets whether any avatar know by the UI is being posed. + /// + bool posingAnyoneOnScrollList(); /// /// Applies the appropriate font-face (such as bold) to the text of the supplied list, to indicate use. @@ -356,8 +381,9 @@ class FSFloaterPoser : public LLFloater LLButton* mToggleSympatheticRotationBtn{ nullptr }; LLButton* mToggleDeltaModeBtn{ nullptr }; LLButton* mRedoChangeBtn{ nullptr }; + LLButton* mSetToTposeButton{ nullptr }; + LLButton* mRecaptureJointsButton{ nullptr }; - LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr }; LLLineEditor* mPoseSaveNameEditor{ nullptr }; LLPanel* mAdvancedParentPnl{ nullptr }; @@ -369,7 +395,6 @@ class FSFloaterPoser : public LLFloater LLPanel* mHandsJointsPnl{ nullptr }; LLPanel* mMiscJointsPnl{ nullptr }; LLPanel* mCollisionVolumesPnl{ nullptr }; - LLPanel* mSaveFilePptionsPnl{ nullptr }; LLPanel* mPosesLoadSavePnl{ nullptr }; }; diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp index c7b7da8d65..4f70b19a2f 100644 --- a/indra/newview/fsposeranimator.cpp +++ b/indra/newview/fsposeranimator.cpp @@ -412,7 +412,69 @@ 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(); +} + +void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) +{ + LLVector3 newRotation = getJointRotation(avatar, joint, translation, negation, CURRENTROTATION); + LLVector3 newPosition = getJointPosition(avatar, joint, true); + LLVector3 newScale = getJointScale(avatar, joint, true); + + setPosingAvatarJoint(avatar, joint, true); + setStartingJointRotation(avatar, &joint, newRotation, translation, negation); + + // recapture of positions and scale does not reset starting values, since this this could result in unwanted residue deformation after posing stops. + setJointPosition(avatar, &joint, newPosition, NONE); + setJointScale(avatar, &joint, newScale, NONE); +} + +void FSPoserAnimator::setStartingJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, + E_BoneAxisTranslation translation, S32 negation) +{ + if (!isAvatarSafeToUse(avatar)) + return; + if (!joint) + return; + + FSPosingMotion* posingMotion = getPosingMotion(avatar); + if (!posingMotion) + return; + + FSPosingMotion::FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName()); + if (!jointPose) + return; + + LLQuaternion rot_quat = translateRotationToQuaternion(translation, negation, rotation); + jointPose->setJointStartRotations(rot_quat); +} + +LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const { LLVector3 vec3; if (!isAvatarSafeToUse(avatar)) @@ -427,11 +489,18 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi return vec3; LLQuaternion rot; - if (forRecapture) - rot = jointPose->getCurrentRotation(); - else - rot = jointPose->getTargetRotation(); - + switch (rotType) + { + case TARGETROTATION: + rot = jointPose->getTargetRotation(); + break; + + case CURRENTROTATION: + default: + rot = jointPose->getCurrentRotation(); + break; + } + return translateRotationFromQuaternion(translation, negation, rot); } @@ -709,6 +778,85 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join 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) { for (size_t index = 0; index != PoserJoints.size(); ++index) @@ -720,7 +868,7 @@ const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const return nullptr; } -bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar *avatar) +bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar) { if (!isAvatarSafeToUse(avatar)) return false; @@ -781,20 +929,18 @@ FSPosingMotion* FSPoserAnimator::getPosingMotion(LLVOAvatar* avatar) const FSPosingMotion* FSPoserAnimator::findOrCreatePosingMotion(LLVOAvatar* avatar) { FSPosingMotion* motion = getPosingMotion(avatar); + if (motion) + return motion; - if (!motion) - { - LLTransactionID mTransactionID; - mTransactionID.generate(); - LLAssetID animationAssetId = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + LLTransactionID mTransactionID; + mTransactionID.generate(); + LLAssetID animationAssetId = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); - if (avatar->registerMotion(animationAssetId, FSPosingMotion::create)) - sAvatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId; + if (avatar->registerMotion(animationAssetId, FSPosingMotion::create)) + sAvatarIdToRegisteredAnimationId[avatar->getID()] = animationAssetId; - return dynamic_cast(avatar->createMotion(animationAssetId)); - } + return dynamic_cast(avatar->createMotion(animationAssetId)); - return motion; } bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 39e56eac7c..d181c847a8 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -32,7 +32,7 @@ /// /// Describes how we will cluster the joints/bones/thingos. -/// Each joint/bone/thingo should have one of these, . +/// Each joint/bone/thingo should have one of these, FSPoserAnimator.PoserJoints. /// typedef enum E_BoneTypes { @@ -56,6 +56,15 @@ typedef enum E_BoneDeflectionStyles DELTAMODE = 3, // each selected joint changes by the same supplied amount relative to their current } E_BoneDeflectionStyles; +/// +/// When getting the rotation of a joint, we can apply different considerations to the rotation. +/// +typedef enum E_BoneRotationType +{ + CURRENTROTATION = 0, // the current rotation the joint has + TARGETROTATION = 1, // the rotation the we want to achieve +} E_BoneRotationType; + /// /// 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. @@ -200,6 +209,14 @@ public: { "mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE, {}, true }, { "mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE, {}, true }, + { "mFaceEar1Left", "mFaceEar1Right", FACE }, + { "mFaceEar2Left", "mFaceEar2Right", FACE }, + { "mFaceEar1Right", "mFaceEar1Left", FACE, {}, true }, + { "mFaceEar2Right", "mFaceEar2Left", FACE, {}, true }, + { "mFaceNoseLeft", "mFaceNoseRight", FACE }, + { "mFaceNoseCenter", "", FACE }, + { "mFaceNoseRight", "mFaceNoseLeft", FACE, {}, true }, + { "mFaceCheekUpperLeft", "mFaceCheekUpperRight", FACE }, { "mFaceCheekLowerLeft", "mFaceCheekLowerRight", FACE }, { "mFaceCheekUpperRight", "mFaceCheekUpperLeft", FACE, {}, true }, @@ -429,11 +446,10 @@ public: /// The avatar whose joint is being queried. /// The joint to determine the rotation for. /// The joint to determine the rotation for. - /// The style of negation to apply to the set. - /// Get the current non-poser rotation, for recapture opportunity. + /// The style of negation to dis-apply to the get. + /// The type of rotation to get from the supplied joint for the supplied avatar. /// The rotation of the requested joint, if determinable, otherwise a default vector. - LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, - bool forRecapture = false) const; + LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation, E_BoneRotationType rotType) const; /// /// Sets the rotation of a joint for the supplied avatar. @@ -443,6 +459,7 @@ public: /// The rotation to set the joint to. /// Any ancilliary action to be taken with the change to be made. /// The axial translation form the supplied joint. + /// The style of negation to apply to the set. void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, E_BoneDeflectionStyles style, E_BoneAxisTranslation translation, S32 negation); @@ -476,6 +493,92 @@ public: /// The avatar whose pose should flip left-right. void flipEntirePose(LLVOAvatar* avatar); + /// + /// Recaptures the rotation, position and scale state of the supplied joint for the supplied avatar. + /// + /// The avatar whose joint is to be recaptured. + /// The joint to recapture. + /// The axial translation form the supplied joint. + /// The style of negation to apply to the recapture. + void recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation); + + /// + /// Sets all of the joint rotations of the supplied avatar to zero. + /// + /// The avatar whose joint rotations should be set to zero. + void setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar); + + /// + /// Determines if the kind of save to perform should be a 'delta' save, or a complete save. + /// + /// The avatar whose pose-rotations are being considered for saving. + /// True if the save should save only 'deltas' to the rotation, otherwise false. + /// + /// 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. + /// + bool posingStartedFromZeroRotations(LLVOAvatar* avatar) const; + + /// + /// Tries to get the rotation, position and scale changes from initial conditions, to save in some export container. + /// + /// The avatar whose pose is being considered for saving. + /// The joint we are considering the save for. + /// The quaternion to store the rotation to save in. + /// The vector to store the position to save in. + /// The vector to store the scale to save in. + /// True if the joint should be saved, otherwise false. + /// + /// 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'). + /// + bool tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, LLVector3* scale); + + /// + /// Loads a joint rotation for the supplied joint on the supplied avatar. + /// + /// The avatar to load the rotation for. + /// The joint to load the rotation for. + /// The rotation to load. + /// + /// 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, + /// or whether its rotation is zero, the result is always the same: just 'add' the supplied rotation to the existing rotation. + /// + void loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, LLVector3 rotation); + + /// + /// Loads a joint position for the supplied joint on the supplied avatar. + /// + /// The avatar to load the position for. + /// The joint to load the position for. + /// Whether to the supplied position as a delta to the current position, or not. + /// The Position to apply to the supplied joint. + /// + /// 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 pose is. + /// 'Legacy' (pre save format version-4) poses we expect to load as absolutes. + /// + void loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position); + + /// + /// Loads a joint scale for the supplied joint on the supplied avatar. + /// + /// The avatar to load the scale for. + /// The joint to load the scale for. + /// Whether to the supplied scale as a delta to the current scale, or not. + /// The scale to apply to the supplied joint. + /// + /// 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. + /// + void loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadScaleAsDelta, LLVector3 scale); + private: /// /// Translates a rotation vector from the UI to a Quaternion for the bone. @@ -519,6 +622,9 @@ public: /// True if the avatar is safe to manipulate, otherwise false. bool isAvatarSafeToUse(LLVOAvatar* avatar) const; + void setStartingJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& rotation, + E_BoneAxisTranslation translation, S32 negation); + /// /// Maps the avatar's ID to the animation registered to them. /// Thus we start/stop the same animation, and get/set the same rotations etc. diff --git a/indra/newview/fsposingmotion.cpp b/indra/newview/fsposingmotion.cpp index 657dc662f3..f0f37a1455 100644 --- a/indra/newview/fsposingmotion.cpp +++ b/indra/newview/fsposingmotion.cpp @@ -249,11 +249,40 @@ bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint) 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() +{ + LLQuaternion zeroQuat; + for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) + { + if (poserJoint_iter->isCollisionVolume()) + continue; + + poserJoint_iter->setJointStartRotations(zeroQuat); + } +} constexpr size_t MaximumUndoQueueLength = 20; /// -/// The constant time interval, in seconds, +/// The constant time interval, in seconds, specifying whether an 'undo' value should be added. /// constexpr std::chrono::duration UndoUpdateInterval = std::chrono::duration(0.3); @@ -489,6 +518,8 @@ void FSPosingMotion::FSJointPose::revertCollisionVolume() joint->setScale(mBeginningScale); } +void FSPosingMotion::FSJointPose::setJointStartRotations(LLQuaternion quat) { mBeginningRotation = mTargetRotation = quat; } + FSPosingMotion::FSJointPose::FSJointPose(LLJoint* joint, bool isCollisionVolume) { mJointState = new LLJointState; diff --git a/indra/newview/fsposingmotion.h b/indra/newview/fsposingmotion.h index f5215b2b0b..c20f3b07c2 100644 --- a/indra/newview/fsposingmotion.h +++ b/indra/newview/fsposingmotion.h @@ -222,6 +222,11 @@ public: /// void revertCollisionVolume(); + /// + /// Sets the beginning and target rotations to the supplied rotation. + /// + void setJointStartRotations(LLQuaternion quat); + /// /// Gets the pointer to the jointstate for the joint this represents. /// @@ -295,6 +300,17 @@ public: /// The unique, per-session, per-character motion identity. LLAssetID motionId() const { return mMotionID; } + /// + /// Gets whether all starting rotations are zero. + /// + /// True if all starting rotations are zero, otherwise false. + bool allStartingRotationsAreZero() const; + + /// + /// Sets all of the non-Collision Volume rotations to zero. + /// + void setAllRotationsToZero(); + private: /// /// The kind of joint state this animation is concerned with changing. diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 0860143ad8..1790bb0984 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -1457,14 +1457,8 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgeLinden")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Linden", "BadgeLinden"); + setBadge("Profile_Badge_Linden", "BadgeLinden", BadgeLocation::bottom); } - // Add Firestorm team badge - else if (FSData::getInstance()->getAgentFlags(avatar_data->avatar_id) != -1) - { - setBadge("Profile_Badge_Team", "BadgeTeam"); - } - // else if (avatar_data->born_on < sl_release) { // Fix LL UI/UX design accident @@ -1472,7 +1466,7 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgeBeta")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Beta", "BadgeBeta"); + setBadge("Profile_Badge_Beta", "BadgeBeta", BadgeLocation::bottom); } else if (customer_lower == "beta_lifetime") { @@ -1481,7 +1475,7 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgeBetaLifetime")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Beta_Lifetime", "BadgeBetaLifetime"); + setBadge("Profile_Badge_Beta_Lifetime", "BadgeBetaLifetime", BadgeLocation::bottom); } else if (customer_lower == "lifetime") { @@ -1490,7 +1484,7 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgeLifetime")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Lifetime", "BadgeLifetime"); + setBadge("Profile_Badge_Lifetime", "BadgeLifetime", BadgeLocation::bottom); } else if (customer_lower == "secondlifetime_premium") { @@ -1499,7 +1493,7 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgePremiumLifetime")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Premium_Lifetime", "BadgePremiumLifetime"); + setBadge("Profile_Badge_Premium_Lifetime", "BadgePremiumLifetime", BadgeLocation::bottom); } else if (customer_lower == "secondlifetime_premium_plus") { @@ -1508,7 +1502,7 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data //getChild("badge_text")->setValue(getString("BadgePremiumPlusLifetime")); //childSetVisible("badge_layout", true); //childSetVisible("partner_spacer_layout", false); - setBadge("Profile_Badge_Pplus_Lifetime", "BadgePremiumPlusLifetime"); + setBadge("Profile_Badge_Pplus_Lifetime", "BadgePremiumPlusLifetime", BadgeLocation::bottom); } else { @@ -1516,14 +1510,22 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data // Fix LL UI/UX design accident //childSetVisible("partner_spacer_layout", true); } + + // Add Firestorm team badge + if (FSData::getInstance()->getAgentFlags(avatar_data->avatar_id) != -1) + { + setBadge("Profile_Badge_Team", "BadgeTeam", BadgeLocation::top); + } + // } // Fix LL UI/UX design accident -void LLPanelProfileSecondLife::setBadge(std::string_view icon_name, std::string_view tooltip) +void LLPanelProfileSecondLife::setBadge(std::string_view icon_name, std::string_view tooltip, BadgeLocation location) { - auto iconctrl = getChild("badge_icon"); + auto iconctrl = getChild(location == BadgeLocation::top ? "top_badge_icon" : "bottom_badge_icon"); iconctrl->setValue(icon_name.data()); iconctrl->setToolTip(getString(tooltip.data())); + childSetVisible(location == BadgeLocation::top ? "top_badge_layout" : "bottom_badge_layout", true); childSetVisible("badge_layout", true); } // diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index f71c0e3db3..d348da069a 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -215,8 +215,13 @@ private: void onCommitProfileImage(const LLUUID& id); // Fix LL UI/UX design accident + enum class BadgeLocation + { + top, + bottom + }; void updateButtons(); - void setBadge(std::string_view icon_name, std::string_view tooltip); + void setBadge(std::string_view icon_name, std::string_view tooltip, BadgeLocation location); private: typedef std::map group_map_t; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6b76a9425c..c81f62ff8d 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3336,6 +3336,8 @@ void login_show() //LLPanelLogin::show( gViewerWindow->getWindowRectScaled(), login_callback, NULL ); FSPanelLogin::show( gViewerWindow->getWindowRectScaled(), login_callback, NULL ); // [FS Login Panel] + + LLNotificationsUtil::add("WhitelistReminder"); // Whitelist reminder } // Callback for when login screen is closed. Option 0 = connect, option 1 = quit. diff --git a/indra/newview/skins/ansastorm/xui/it/floater_camera.xml b/indra/newview/skins/ansastorm/xui/it/floater_camera.xml new file mode 100644 index 0000000000..c04d0a3335 --- /dev/null +++ b/indra/newview/skins/ansastorm/xui/it/floater_camera.xml @@ -0,0 +1,41 @@ + + + + Ruota la camera intorno all'inquadratura + + + Avvicina la camera nell'inquadratura + + + Muovi la camera su, giù, a sinistra e a destra + + + Vista Oggetto + + + Usa Preset + + + + + - + follows="all" + background_visible="false" + height="299" + layout="topleft" + left="0" + title="Presets" + name="hands_presets_panel" + top="0" + width="481"> + column_padding="2" + height="275" + follows="all" + can_sort="false" + layout="topleft" + left="2" + width="479" + multi_select="false" + name="hand_presets_scroll" + top="0"> - + label="Preset Name" + name="name" + relative_width="0.9"/> + + + follows="all" + background_visible="false" + height="299" + layout="topleft" + left="0" + title="Misc" + name="misc_joints_panel" + top="0" + width="481"> + column_padding="2" + height="300" + follows="all" + can_sort="false" + layout="topleft" + left="2" + width="479" + multi_select="true" + name="misc_joints_scroll" + top="0"> + label="" + name="icon" + relative_width="0.1"/> + label="Body Part" + name="joint" + relative_width="0.9"/> + follows="all" + background_visible="false" + height="299" + layout="topleft" + left="0" + title="Physics" + name="collision_volumes_panel" + top="0" + width="481"> + column_padding="2" + height="300" + follows="all" + can_sort="false" + layout="topleft" + left="2" + width="479" + multi_select="true" + name="collision_volumes_scroll" + top="0"> + label="" + name="icon" + relative_width="0.1"/> + label="Physics" + name="joint" + relative_width="0.9"/> + + + + + + + + + + + + + + + Trackpad Sensitivity: + + + + + + - Rotation: - + follows="left|top|bottom" + height="290" + background_visible="false" + layout="topleft" + mouse_opaque="false" + name="trackball_panel" + enabled="false" + top="0" + width="160" + left_pad="2"> + + Rotation: + + + follows="left|top|bottom" + height="22" + background_visible="false" + layout="topleft" + mouse_opaque="false" + left="0" + top_pad="2" + name="trackball_button_panel" + width="232"> - Up/Down: - - Left/Right: - - Roll: - + + Up/Down: + + + + Left/Right: + + + + Roll: + + + follows="top|left|bottom" + height="290" + background_visible="false" + layout="topleft" + mouse_opaque="false" + left_pad="2" + visible="false" + name="poses_loadSave" + top="0" + width="225"> + column_padding="0" + height="268" + follows="left|top|right" + layout="topleft" + tool_tip="Load a pose for who you are animating." + width="225" + multi_select="true" + name="poses_scroll" + top="0"> + label="Pose Name" + name="name"/> + follows="top|left" + layout="topleft" + height="21" + top_pad="-2" + commit_on_focus_lost="false" + label="Enter Pose Name To Save..." + name="pose_save_name" + width="225"/> + follows="top|left|right" + layout="topleft" + height="22" + auto_resize="false" + name="button_controls_layout" + width="800"> + follows="left|top|bottom" + height="100" + background_visible="false" + layout="topleft" + mouse_opaque="false" + top="0" + left="0" + name="button_controls_panel" + width="800"> - - + follows="left|top|bottom" + height="22" + background_visible="false" + layout="topleft" + mouse_opaque="false" + left_pad="0" + top_delta="0" + name="advbutton_spacer_panel" + width="36"/> + follows="left|top|bottom" + height="22" + background_visible="false" + layout="topleft" + mouse_opaque="false" + left_pad="0" + top_delta="0" + name="button_spacer_panel" + width="58"/> + height="21" + follows="top|left" + layout="topleft" + label="Load Pose" + enabled="false" + visible="false" + tool_tip="Load the currently selected pose." + image_overlay="Icon_Undock_Foreground" + image_overlay_alignment="left" + image_hover_unselected="Toolbar_Middle_Over" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + menu_filename="menu_fs_poser_poses_btn.xml" + menu_position="topright" + name="load_poses_button" + left_pad="1" + top_delta="0" + width="97"/> + follows="top|left|right" + layout="topleft" + height="97" + auto_resize="false" + name="advanced_controls_layout" + width="600"> + follows="top|left|right" + height="97" + left="0" + layout="all" + enabled="false" + background_visible="false" + mouse_opaque="false" + visible="false" + name="advanced_parent_panel" + width="555"> + follows="all" + halign="center" + height="97" + layout="topleft" + name="modifier_tabs" + tab_height="20" + tab_group="1" + tab_position="top" + top_pad="0" + width="555"> + follows="all" + background_visible="false" + height="97" + layout="topleft" + left="0" + title="Body Part Position" + name="position_panel" + top="0" + width="555"> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Position X:" + label_width="70" + layout="topleft" + left="10" + max_val="0.5" + min_val="-0.5" + name="Advanced_Position_X" + top_pad="6" + width="380" > + function="Poser.Advanced.PositionSet"/> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Position Y:" + label_width="70" + layout="topleft" + left_delta="0" + max_val="0.5" + min_val="-0.5" + name="Advanced_Position_Y" + top_pad="1" + width="380" > + function="Poser.Advanced.PositionSet"/> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Position Z:" + label_width="70" + layout="topleft" + left_delta="0" + max_val="0.5" + min_val="-0.5" + name="Advanced_Position_Z" + top_pad="1" + width="380" > + function="Poser.Advanced.PositionSet"/> + follows="all" + background_visible="false" + height="97" + layout="topleft" + left="0" + title="Body Part Scale" + name="scale_panel" + top="0" + width="607"> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Scale X:" + label_width="70" + layout="topleft" + left="10" + max_val="2" + min_val="0" + name="Advanced_Scale_X" + top_pad="6" + width="380" > + function="Poser.Advanced.ScaleSet"/> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Scale Y:" + label_width="70" + layout="topleft" + left_delta="0" + max_val="2" + min_val="0" + name="Advanced_Scale_Y" + top_pad="1" + width="380" > + function="Poser.Advanced.ScaleSet"/> + decimal_digits="3" + can_edit_text="true" + follows="left|top" + height="14" + increment="0.001" + initial_value="0" + label="Scale Z:" + label_width="70" + layout="topleft" + left_delta="0" + max_val="2" + min_val="0" + name="Advanced_Scale_Z" + top_pad="1" + width="380" > + function="Poser.Advanced.ScaleSet"/> - - - - - diff --git a/indra/newview/skins/default/xui/en/menu_pie_object.xml b/indra/newview/skins/default/xui/en/menu_pie_object.xml index 05cb25a846..354e52342e 100644 --- a/indra/newview/skins/default/xui/en/menu_pie_object.xml +++ b/indra/newview/skins/default/xui/en/menu_pie_object.xml @@ -339,6 +339,7 @@ + + + + + + + + - - - - - - - diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index cf2d595375..064f22177d 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -14663,4 +14663,21 @@ Camera reset might be inhibited by the following objects: + +To improve [APP_NAME]'s performance, please whitelist it. + +Some antivirus programs may mistakenly block parts of [APP_NAME], slowing down its performance and causing some features to malfunction. To prevent these issues, we strongly recommend adding [APP_NAME] to your antivirus program's whitelist (or exclusion list). This will ensure the viewer runs smoothly. + +For detailed instructions on how to whitelist [APP_NAME] - including a list of files and folders to exclude - please visit our guide: + +https://wiki.firestormviewer.org/antivirus_whitelisting + + + diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml index fb2320ccd8..0f494440d8 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml @@ -478,16 +478,55 @@ visible="false" auto_resize="false" user_resize="false"> - + orientation="vertical"> + + + + + + + - + Avatar non valido - Preparazione delle -texture + Texture +bake Texture -composte +composite -