From 90c7684112714fd5ca2c8d73d8ca9bef3fc1e5d6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 10 Apr 2025 17:57:16 +0300 Subject: #3758 add support for highlighted segments --- indra/newview/llappviewer.cpp | 2 +- indra/newview/llpanelprofile.cpp | 2 +- indra/newview/llstartup.cpp | 2 +- indra/newview/skins/default/colors.xml | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index edc70030b4..7e7aa521d3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -475,7 +475,7 @@ static void deferred_ui_audio_callback(const LLUUID& uuid) bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) { - if(!match || !base || base->getPlainText()) + if (!match || match->getSkipProfileIcon() || !base || base->getPlainText()) return false; LLUUID match_id = match->getID(); diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 08605f7cf4..c81746a48a 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -328,7 +328,7 @@ public: } const std::string verb = params[1].asString(); - if (verb == "about") + if (verb == "about" || verb == "mention") { LLAvatarActions::showProfile(avatar_id); return true; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3973036cc6..4f60f98b49 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3566,7 +3566,7 @@ bool process_login_success_response() // Agent id needed for parcel info request in LLUrlEntryParcel // to resolve parcel name. - LLUrlEntryParcel::setAgentID(gAgentID); + LLUrlEntryBase::setAgentID(gAgentID); text = response["session_id"].asString(); if(!text.empty()) gAgentSessionID.set(text); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index f0af4acf20..cb190d3d34 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -1000,4 +1000,10 @@ + + -- cgit v1.2.3 From 3e46d707a243e91046b3ab0af8f844d7f40f77b4 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 18 Apr 2025 17:48:02 +0300 Subject: #3758 initial chat mention support --- indra/newview/CMakeLists.txt | 2 + indra/newview/llavatarlist.cpp | 18 +- indra/newview/llavatarlist.h | 7 +- indra/newview/llavatarlistitem.cpp | 17 +- indra/newview/llavatarlistitem.h | 3 +- indra/newview/llfloaterchatmentionpicker.cpp | 183 +++++++++++++++++++++ indra/newview/llfloaterchatmentionpicker.h | 58 +++++++ indra/newview/llfloaterimnearbychat.cpp | 2 +- indra/newview/llfloaterimsession.cpp | 2 +- indra/newview/llfloaterimsessiontab.cpp | 2 + indra/newview/llviewerfloaterreg.cpp | 2 + .../default/xui/en/floater_chat_mention_picker.xml | 31 ++++ 12 files changed, 311 insertions(+), 16 deletions(-) create mode 100644 indra/newview/llfloaterchatmentionpicker.cpp create mode 100644 indra/newview/llfloaterchatmentionpicker.h create mode 100644 indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fff1597fd9..98151e2f4d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -201,6 +201,7 @@ set(viewer_SOURCE_FILES llfloatercamera.cpp llfloatercamerapresets.cpp llfloaterchangeitemthumbnail.cpp + llfloaterchatmentionpicker.cpp llfloaterchatvoicevolume.cpp llfloaterclassified.cpp llfloatercolorpicker.cpp @@ -870,6 +871,7 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamerapresets.h llfloaterchangeitemthumbnail.h + llfloaterchatmentionpicker.h llfloatercamera.h llfloaterchatvoicevolume.h llfloaterclassified.h diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 8f858fe4e1..f206474e71 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -141,6 +141,7 @@ LLAvatarList::LLAvatarList(const Params& p) , mShowSpeakingIndicator(p.show_speaking_indicator) , mShowPermissions(p.show_permissions_granted) , mShowCompleteName(false) +, mForceCompleteName(false) { setCommitOnSelectionChange(true); @@ -177,7 +178,7 @@ void LLAvatarList::setShowIcons(std::string param_name) std::string LLAvatarList::getAvatarName(LLAvatarName av_name) { - return mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName(); + return mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName(); } // virtual @@ -364,7 +365,7 @@ void LLAvatarList::updateAvatarNames() for( std::vector::const_iterator it = items.begin(); it != items.end(); it++) { LLAvatarListItem* item = static_cast(*it); - item->setShowCompleteName(mShowCompleteName); + item->setShowCompleteName(mShowCompleteName, mForceCompleteName); item->updateAvatarName(); } mNeedUpdateNames = false; @@ -404,6 +405,11 @@ boost::signals2::connection LLAvatarList::setItemDoubleClickCallback(const mouse return mItemDoubleClickSignal.connect(cb); } +boost::signals2::connection LLAvatarList::setItemClickedCallback(const mouse_signal_t::slot_type& cb) +{ + return mItemClickedSignal.connect(cb); +} + //virtual S32 LLAvatarList::notifyParent(const LLSD& info) { @@ -418,7 +424,7 @@ S32 LLAvatarList::notifyParent(const LLSD& info) void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is_online, EAddPosition pos) { LLAvatarListItem* item = new LLAvatarListItem(); - item->setShowCompleteName(mShowCompleteName); + item->setShowCompleteName(mShowCompleteName, mForceCompleteName); // This sets the name as a side effect item->setAvatarId(id, mSessionID, mIgnoreOnlineStatus); item->setOnline(mIgnoreOnlineStatus ? true : is_online); @@ -432,6 +438,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoubleClicked, this, _1, _2, _3, _4)); + item->setMouseDownCallback(boost::bind(&LLAvatarList::onItemClicked, this, _1, _2, _3, _4)); addItem(item, id, pos); } @@ -550,6 +557,11 @@ void LLAvatarList::onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) mItemDoubleClickSignal(ctrl, x, y, mask); } +void LLAvatarList::onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mItemClickedSignal(ctrl, x, y, mask); +} + bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const { const LLAvatarListItem* avatar_item1 = dynamic_cast(item1); diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index af5bfefcde..f99da93a3d 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -96,11 +96,13 @@ public: boost::signals2::connection setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb); + boost::signals2::connection setItemClickedCallback(const mouse_signal_t::slot_type& cb); + virtual S32 notifyParent(const LLSD& info); void handleDisplayNamesOptionChanged(); - void setShowCompleteName(bool show) { mShowCompleteName = show;}; + void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force; }; protected: void refresh(); @@ -113,6 +115,7 @@ protected: void updateLastInteractionTimes(); void rebuildNames(); void onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); + void onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); void updateAvatarNames(); private: @@ -127,6 +130,7 @@ private: bool mShowSpeakingIndicator; bool mShowPermissions; bool mShowCompleteName; + bool mForceCompleteName; LLTimer* mLITUpdateTimer; // last interaction time update timer std::string mIconParamName; @@ -138,6 +142,7 @@ private: commit_signal_t mRefreshCompleteSignal; mouse_signal_t mItemDoubleClickSignal; + mouse_signal_t mItemClickedSignal; }; /** Abstract comparator for avatar items */ diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 880910d18e..6ef45ed160 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -78,6 +78,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) mShowProfileBtn(true), mShowPermissions(false), mShowCompleteName(false), + mForceCompleteName(false), mHovered(false), mAvatarNameCacheConnection(), mGreyOutUsername("") @@ -324,13 +325,11 @@ void LLAvatarListItem::setShowProfileBtn(bool show) void LLAvatarListItem::showSpeakingIndicator(bool visible) { - // Already done? Then do nothing. - if (mSpeakingIndicator->getVisible() == (bool)visible) - return; -// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976 -// probably this method should be totally removed. -// mSpeakingIndicator->setVisible(visible); -// updateChildren(); + if (mSpeakingIndicator) + { + mSpeakingIndicator->setIsActiveChannel(visible); + mSpeakingIndicator->setShowParticipantsSpeaking(visible); + } } void LLAvatarListItem::setAvatarIconVisible(bool visible) @@ -417,8 +416,8 @@ void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name) mAvatarNameCacheConnection.disconnect(); mGreyOutUsername = ""; - std::string name_string = mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName(); - if(av_name.getCompleteName() != av_name.getUserName()) + std::string name_string = mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName(); + if(av_name.getCompleteName(false, mForceCompleteName) != av_name.getUserName()) { mGreyOutUsername = "[ " + av_name.getUserName(true) + " ]"; LLStringUtil::toLower(mGreyOutUsername); diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 2e4c597d30..2ec7a41055 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -106,7 +106,7 @@ public: void setShowPermissions(bool show) { mShowPermissions = show; }; void showLastInteractionTime(bool show); void setAvatarIconVisible(bool visible); - void setShowCompleteName(bool show) { mShowCompleteName = show;}; + void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force;}; const LLUUID& getAvatarId() const; std::string getAvatarName() const; @@ -220,6 +220,7 @@ private: bool mHovered; bool mShowCompleteName; + bool mForceCompleteName; std::string mGreyOutUsername; void fetchAvatarName(); diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp new file mode 100644 index 0000000000..dda2cd83f6 --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -0,0 +1,183 @@ +/** + * @file llfloaterchatmentionpicker.cpp + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterchatmentionpicker.h" + +#include "llavatarlist.h" +#include "llfloaterimcontainer.h" +#include "llchatmentionhelper.h" +#include "llparticipantlist.h" + +LLUUID LLFloaterChatMentionPicker::sSessionID(LLUUID::null); + +LLFloaterChatMentionPicker::LLFloaterChatMentionPicker(const LLSD& key) +: LLFloater(key), mAvatarList(NULL) +{ + // This floater should hover on top of our dependent (with the dependent having the focus) + setFocusStealsFrontmost(false); + setBackgroundVisible(false); + setAutoFocus(false); +} + +bool LLFloaterChatMentionPicker::postBuild() +{ + mAvatarList = getChild("avatar_list"); + mAvatarList->setShowCompleteName(true, true); + mAvatarList->setFocusOnItemClicked(false); + mAvatarList->setItemClickedCallback([this](LLUICtrl* ctrl, S32 x, S32 y, MASK mask) + { + if (LLAvatarListItem* item = dynamic_cast(ctrl)) + { + selectResident(item->getAvatarId()); + } + }); + mAvatarList->setRefreshCompleteCallback([this](LLUICtrl* ctrl, const LLSD& param) + { + if (mAvatarList->numSelected() == 0) + { + mAvatarList->selectFirstItem(); + } + }); + + return LLFloater::postBuild(); +} + +void LLFloaterChatMentionPicker::onOpen(const LLSD& key) +{ + buildAvatarList(); + mAvatarList->setNameFilter(key.has("av_name") ? key["av_name"].asString() : ""); + + gFloaterView->adjustToFitScreen(this, false); +} + +uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds() +{ + LLParticipantList* item = dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID)); + if (!item) + { + LL_WARNS() << "Participant list is missing" << LL_ENDL; + return {}; + } + + uuid_vec_t avatar_ids; + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + if (participant_model) + { + avatar_ids.push_back(participant_model->getUUID()); + } + current_participant_model++; + } + return avatar_ids; +} + +void LLFloaterChatMentionPicker::buildAvatarList() +{ + uuid_vec_t& avatar_ids = mAvatarList->getIDs(); + avatar_ids = getParticipantIds(); + updateAvatarList(avatar_ids); + mAvatarList->setDirty(); +} + +void LLFloaterChatMentionPicker::selectResident(const LLUUID& id) +{ + if (id.isNull()) + return; + + setValue(stringize("secondlife:///app/agent/", id.asString(), "/mention ")); + onCommit(); + LLChatMentionHelper::instance().hideHelper(); +} + +void LLFloaterChatMentionPicker::onClose(bool app_quitting) +{ + if (!app_quitting) + { + LLChatMentionHelper::instance().hideHelper(); + } +} + +bool LLFloaterChatMentionPicker::handleKey(KEY key, MASK mask, bool called_from_parent) +{ + if (mask == MASK_NONE) + { + switch (key) + { + case KEY_UP: + case KEY_DOWN: + return mAvatarList->handleKey(key, mask, called_from_parent); + case KEY_RETURN: + selectResident(mAvatarList->getSelectedUUID()); + return true; + case KEY_ESCAPE: + LLChatMentionHelper::instance().hideHelper(); + return true; + case KEY_LEFT: + case KEY_RIGHT: + return true; + default: + break; + } + } + return LLFloater::handleKey(key, mask, called_from_parent); +} + +void LLFloaterChatMentionPicker::goneFromFront() +{ + LLChatMentionHelper::instance().hideHelper(); +} + +void LLFloaterChatMentionPicker::updateSessionID(LLUUID session_id) +{ + sSessionID = session_id; + + LLParticipantList* item = dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID)); + if (!item) + { + LL_WARNS() << "Participant list is missing" << LL_ENDL; + return; + } + + uuid_vec_t avatar_ids = getParticipantIds(); + updateAvatarList(avatar_ids); +} + +void LLFloaterChatMentionPicker::updateAvatarList(uuid_vec_t& avatar_ids) +{ + std::vector av_names; + for (auto& id : avatar_ids) + { + LLAvatarName av_name; + LLAvatarNameCache::get(id, &av_name); + av_names.push_back(utf8str_tolower(av_name.getAccountName())); + av_names.push_back(utf8str_tolower(av_name.getDisplayName())); + } + LLChatMentionHelper::instance().updateAvatarList(av_names); +} diff --git a/indra/newview/llfloaterchatmentionpicker.h b/indra/newview/llfloaterchatmentionpicker.h new file mode 100644 index 0000000000..8d221d7a89 --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.h @@ -0,0 +1,58 @@ +/** + * @file llfloaterchatmentionpicker.h + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLFLOATERCHATMENTIONPICKER_H +#define LLFLOATERCHATMENTIONPICKER_H + +#include "llfloater.h" + +class LLAvatarList; + +class LLFloaterChatMentionPicker : public LLFloater +{ +public: + LLFloaterChatMentionPicker(const LLSD& key); + + virtual bool postBuild() override; + virtual void goneFromFront() override; + + void buildAvatarList(); + + static uuid_vec_t getParticipantIds(); + static void updateSessionID(LLUUID session_id); + static void updateAvatarList(uuid_vec_t& avatar_ids); + +private: + + void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; + virtual bool handleKey(KEY key, MASK mask, bool called_from_parent) override; + void selectResident(const LLUUID& id); + + static LLUUID sSessionID; + LLAvatarList* mAvatarList; +}; + +#endif diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index db6f9ac22a..b649514bff 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -586,7 +586,7 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) { if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 185274981b..84a9fad708 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -251,7 +251,7 @@ void LLFloaterIMSession::sendMsgFromInputEditor() { if (mInputEditor) { - LLWString text = mInputEditor->getWText(); + LLWString text = mInputEditor->getConvertedText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 50e765c236..96aac8c1e6 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -35,6 +35,7 @@ #include "llavatariconctrl.h" #include "llchatentry.h" #include "llchathistory.h" +#include "llfloaterchatmentionpicker.h" #include "llchiclet.h" #include "llchicletbar.h" #include "lldraghandle.h" @@ -485,6 +486,7 @@ void LLFloaterIMSessionTab::onFocusReceived() LLIMModel::instance().sendNoUnreadMessages(mSessionID); } + LLFloaterChatMentionPicker::updateSessionID(mSessionID); super::onFocusReceived(); } diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 95c2a77ba8..4d9c2f3281 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,7 @@ #include "llfloatercamera.h" #include "llfloatercamerapresets.h" #include "llfloaterchangeitemthumbnail.h" +#include "llfloaterchatmentionpicker.h" #include "llfloaterchatvoicevolume.h" #include "llfloaterclassified.h" #include "llfloaterconversationlog.h" @@ -353,6 +354,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); + LLFloaterReg::add("chat_mention_picker", "floater_chat_mention_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml new file mode 100644 index 0000000000..bbad99f932 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml @@ -0,0 +1,31 @@ + + + + -- cgit v1.2.3 From 44bbdc549a9a530850ffbb02c06059517c37d82d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 21 Apr 2025 15:56:01 +0300 Subject: #3488 Reduce locking 1. 'sActive' variables are atomic, no locks needed 2. Fix trylocks. There are internal locks inside loadMeshLOD so without checking locks 3 and 4 viewer would be locked on each loadMeshLOD, potentially making main thread wait for threads to unlock. --- indra/newview/llmeshrepository.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 48c80842b9..3724ec26df 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1809,42 +1809,36 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) //static void LLMeshRepoThread::incActiveLODRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); ++LLMeshRepoThread::sActiveLODRequests; } //static void LLMeshRepoThread::decActiveLODRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); --LLMeshRepoThread::sActiveLODRequests; } //static void LLMeshRepoThread::incActiveHeaderRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); ++LLMeshRepoThread::sActiveHeaderRequests; } //static void LLMeshRepoThread::decActiveHeaderRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); --LLMeshRepoThread::sActiveHeaderRequests; } //static void LLMeshRepoThread::incActiveSkinRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); ++LLMeshRepoThread::sActiveSkinRequests; } //static void LLMeshRepoThread::decActiveSkinRequests() { - LLMutexLock lock(gMeshRepo.mThread->mMutex); --LLMeshRepoThread::sActiveSkinRequests; } @@ -4495,13 +4489,20 @@ void LLMeshRepository::notifyLoadedMeshes() { LLMutexTrylock lock1(mMeshMutex); LLMutexTrylock lock2(mThread->mMutex); + LLMutexTrylock lock3(mThread->mHeaderMutex); + LLMutexTrylock lock4(mThread->mPendingMutex); static U32 hold_offs(0); - if (! lock1.isLocked() || ! lock2.isLocked()) + if (! lock1.isLocked() || ! lock2.isLocked() || ! lock3.isLocked() || ! lock4.isLocked()) { // If we can't get the locks, skip and pick this up later. + // Eventually thread queue will be free enough ++hold_offs; sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); + if (hold_offs > 4) + { + LL_WARNS_ONCE() << "High mesh thread holdoff" << LL_ENDL; + } return; } hold_offs = 0; @@ -4598,8 +4599,6 @@ void LLMeshRepository::notifyLoadedMeshes() std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, mPendingRequests.end(), PendingRequestBase::CompareScoreGreater()); } - LLMutexTrylock lock3(mThread->mHeaderMutex); - LLMutexTrylock lock4(mThread->mPendingMutex); while (!mPendingRequests.empty() && push_count > 0) { std::unique_ptr& req_p = mPendingRequests.front(); -- cgit v1.2.3 From 87b6428f5537238c970cabbc30e057914935a50c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 21 Apr 2025 16:59:29 +0300 Subject: #3870 Added joint initialization for LLVOAvatarSelf Sometimes mesh thread crashes when allocating joints --- indra/newview/llmeshrepository.cpp | 1 + indra/newview/llvoavatar.cpp | 10 ++++++++++ indra/newview/llvoavatar.h | 1 + indra/newview/llvoavatarself.cpp | 2 ++ 4 files changed, 14 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 3724ec26df..663afe64a9 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4549,6 +4549,7 @@ void LLMeshRepository::notifyLoadedMeshes() if (mPendingRequests.size() > push_count) { + LL_PROFILE_ZONE_NAMED("Mesh score_map"); // More requests than the high-water limit allows so // sort and forward the most important. diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 3306289f51..d9a3ec3004 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -6375,6 +6375,16 @@ LLJoint *LLVOAvatar::getJoint( S32 joint_num ) return pJoint; } +void LLVOAvatar::initAllJoints() +{ + getJointAliases(); + for (auto& alias : mJointAliasMap) + { + mJointMap[alias.first] = mRoot->findJoint(alias.second); + } + // ignore mScreen and mRoot +} + //----------------------------------------------------------------------------- // getRiggedMeshID // diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index a2232d21a2..ab27c5752d 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -204,6 +204,7 @@ public: virtual LLJoint* getJoint(const std::string &name); LLJoint* getJoint(S32 num); + void initAllJoints(); //if you KNOW joint_num is a valid animated joint index, use getSkeletonJoint for efficiency inline LLJoint* getSkeletonJoint(S32 joint_num) { return mSkeleton[joint_num]; } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index f23af5afa4..90ff4067f2 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -225,6 +225,8 @@ void LLVOAvatarSelf::initInstance() doPeriodically(update_avatar_rez_metrics, 5.0); doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0); + initAllJoints(); // mesh thread uses LLVOAvatarSelf as a joint source + mInitFlags |= 1<<2; } -- cgit v1.2.3 From b30283e5ef164ed84ad47d7d6f52ebc1c6c7d558 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 22 Apr 2025 19:45:48 +0300 Subject: #3960 Fix expensive mesh thread score calculations --- indra/newview/llmeshrepository.cpp | 189 +++++++++++++++++-------------------- indra/newview/llmeshrepository.h | 68 +++++++++++-- 2 files changed, 145 insertions(+), 112 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 663afe64a9..851107b3be 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -544,6 +544,66 @@ bool RequestStats::isDelayed() const return mTimer.getStarted() && !mTimer.hasExpired(); } +F32 calculate_score(LLVOVolume* object) +{ + if (!object) + { + return -1.f; + } + LLDrawable* drawable = object->mDrawable; + if (!drawable) + { + return -1; + } + if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) + { + LLVOAvatar* avatar = object->getAvatar(); + LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; + if (avatar && av_drawable) + { + // See LLVOVolume::calcLOD() + F32 radius; + if (avatar->isControlAvatar()) + { + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec() * 0.5f; + } + else + { + // Volume in a rigged mesh attached to a regular avatar. + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec(); + + if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) + { + // slightly deprioritize avatars that are still receiving data + radius *= 0.9f; + } + } + return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); + } + } + return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); +} + +void PendingRequestBase::updateScore() +{ + mScore = 0; + if (mTrackedData) + { + for (LLVOVolume* volume : mTrackedData->mVolumes) + { + F32 cur_score = calculate_score(volume); + if (cur_score > 0) + { + mScore = llmax(mScore, cur_score); + } + } + } +} + LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material) { LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); @@ -2220,7 +2280,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) { - gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info + gMeshRepo.mLoadingSkins[mesh_id]; // add an empty vector to indicate to main thread that we are loading skin info } } @@ -4212,13 +4272,13 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) { for (auto& param : lod) { - vector_replace_with_last(param.second, vobj); + vector_replace_with_last(param.second.mVolumes, vobj); } } for (auto& skin_pair : mLoadingSkins) { - vector_replace_with_last(skin_pair.second, vobj); + vector_replace_with_last(skin_pair.second.mVolumes, vobj); } } @@ -4241,16 +4301,17 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id); if (iter != mLoadingMeshes[new_lod].end()) { //request pending for this mesh, append volume id to list - auto it = std::find(iter->second.begin(), iter->second.end(), vobj); - if (it == iter->second.end()) { - iter->second.push_back(vobj); + auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), vobj); + if (it == iter->second.mVolumes.end()) { + iter->second.addVolume(vobj); } } else { //first request for this mesh - mLoadingMeshes[new_lod][mesh_id].push_back(vobj); - mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod)); + std::shared_ptr request(new PendingRequestLOD(mesh_params, new_lod)); + mPendingRequests.emplace_back(request); + mLoadingMeshes[new_lod][mesh_id].initData(vobj, request); LLMeshRepository::sLODPending++; } } @@ -4309,50 +4370,6 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para return new_lod; } -F32 calculate_score(LLVOVolume* object) -{ - if (!object) - { - return -1.f; - } - LLDrawable* drawable = object->mDrawable; - if (!drawable) - { - return -1; - } - if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) - { - LLVOAvatar* avatar = object->getAvatar(); - LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; - if (avatar && av_drawable) - { - // See LLVOVolume::calcLOD() - F32 radius; - if (avatar->isControlAvatar()) - { - const LLVector3* box = avatar->getLastAnimExtents(); - LLVector3 diag = box[1] - box[0]; - radius = diag.magVec() * 0.5f; - } - else - { - // Volume in a rigged mesh attached to a regular avatar. - const LLVector3* box = avatar->getLastAnimExtents(); - LLVector3 diag = box[1] - box[0]; - radius = diag.magVec(); - - if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) - { - // slightly deprioritize avatars that are still receiving data - radius *= 0.9f; - } - } - return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); - } - } - return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); -} - void LLMeshRepository::notifyLoadedMeshes() { //called from main thread LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); @@ -4549,51 +4566,14 @@ void LLMeshRepository::notifyLoadedMeshes() if (mPendingRequests.size() > push_count) { - LL_PROFILE_ZONE_NAMED("Mesh score_map"); + LL_PROFILE_ZONE_NAMED("Mesh score update"); // More requests than the high-water limit allows so // sort and forward the most important. - //calculate "score" for pending requests - - //create score map - std::map score_map; - - for (U32 i = 0; i < LLVolumeLODGroup::NUM_LODS; ++i) - { - for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) - { - F32 max_score = 0.f; - for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) - { - F32 cur_score = calculate_score(*obj_iter); - if (cur_score >= 0.f) - { - max_score = llmax(max_score, cur_score); - } - } - - score_map[iter->first] = max_score; - } - } - for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter) - { - F32 max_score = 0.f; - for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) - { - F32 cur_score = calculate_score(*obj_iter); - if (cur_score >= 0.f) - { - max_score = llmax(max_score, cur_score); - } - } - - score_map[iter->first] = max_score; - } - - //set "score" for pending requests - for (std::unique_ptr& req_p : mPendingRequests) + // update "score" for pending requests + for (std::shared_ptr& req_p : mPendingRequests) { - req_p->setScore(score_map[req_p->getId()]); + req_p->checkScore(); } //sort by "score" @@ -4602,7 +4582,9 @@ void LLMeshRepository::notifyLoadedMeshes() } while (!mPendingRequests.empty() && push_count > 0) { - std::unique_ptr& req_p = mPendingRequests.front(); + std::shared_ptr& req_p = mPendingRequests.front(); + // todo: check hasTrackedData here and erase request if none + // since this is supposed to mean that request was removed switch (req_p->getRequestType()) { case MESH_REQUEST_LOD: @@ -4657,7 +4639,7 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info) skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID); if (iter != mLoadingSkins.end()) { - for (LLVOVolume* vobj : iter->second) + for (LLVOVolume* vobj : iter->second.mVolumes) { if (vobj) { @@ -4673,7 +4655,7 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id) skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); if (iter != mLoadingSkins.end()) { - for (LLVOVolume* vobj : iter->second) + for (LLVOVolume* vobj : iter->second.mVolumes) { if (vobj) { @@ -4737,7 +4719,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } //notify waiting LLVOVolume instances that their requested mesh is available - for (LLVOVolume* vobj : obj_iter->second) + for (LLVOVolume* vobj : obj_iter->second.mVolumes) { if (vobj) { @@ -4767,7 +4749,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); } - for (LLVOVolume* vobj : obj_iter->second) + for (LLVOVolume* vobj : obj_iter->second.mVolumes) { if (vobj) { @@ -4810,16 +4792,17 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); if (iter != mLoadingSkins.end()) { //request pending for this mesh, append volume id to list - auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj); - if (it == iter->second.end()) { - iter->second.push_back(requesting_obj); + auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), requesting_obj); + if (it == iter->second.mVolumes.end()) { + iter->second.addVolume(requesting_obj); } } else { //first request for this mesh - mLoadingSkins[mesh_id].push_back(requesting_obj); - mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); + std::shared_ptr request(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); + mLoadingSkins[mesh_id].initData(requesting_obj, request); + mPendingRequests.emplace_back(request); } } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b9acb3573f..0847c29d0d 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -168,7 +168,6 @@ public: void submitRequest(Request* request); static S32 llcdCallback(const char*, S32, S32); - void cancel(); void setMeshData(LLCDMeshData& mesh, bool vertex_based); void doDecomposition(); @@ -206,19 +205,19 @@ private: LLFrameTimer mTimer; }; - +class MeshLoadData; class PendingRequestBase { public: struct CompareScoreGreater { - bool operator()(const std::unique_ptr& lhs, const std::unique_ptr& rhs) + bool operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs) { return lhs->mScore > rhs->mScore; // greatest = first } }; - PendingRequestBase() : mScore(0.f) {}; + PendingRequestBase() : mScore(0.f), mTrackedData(nullptr), mScoreDirty(true) {}; virtual ~PendingRequestBase() {} bool operator<(const PendingRequestBase& rhs) const @@ -226,14 +225,34 @@ public: return mId < rhs.mId; } - void setScore(F32 score) { mScore = score; } F32 getScore() const { return mScore; } + void checkScore() + { + constexpr F32 EXPIRE_TIME_SECS = 8.f; + if (mScoreTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS || mScoreDirty) + { + updateScore(); + mScoreDirty = false; + mScoreTimer.reset(); + } + }; + LLUUID getId() const { return mId; } virtual EMeshRequestType getRequestType() const = 0; + void trackData(MeshLoadData* data) { mTrackedData = data; mScoreDirty = true; } + void untrackData() { mTrackedData = nullptr; } + bool hasTrackedData() { return mTrackedData != nullptr; } + void setScoreDirty() { mScoreDirty = true; } + protected: - F32 mScore; + void updateScore(); + LLUUID mId; + F32 mScore; + bool mScoreDirty; + LLTimer mScoreTimer; + MeshLoadData* mTrackedData; }; class PendingRequestLOD : public PendingRequestBase @@ -267,6 +286,37 @@ private: EMeshRequestType mRequestType; }; + +class MeshLoadData +{ +public: + MeshLoadData() {} + ~MeshLoadData() + { + if (std::shared_ptr request = mRequest.lock()) + { + request->untrackData(); + } + } + void initData(LLVOVolume* vol, std::shared_ptr& request) + { + mVolumes.push_back(vol); + request->trackData(this); + mRequest = request; + } + void addVolume(LLVOVolume* vol) + { + mVolumes.push_back(vol); + if (std::shared_ptr request = mRequest.lock()) + { + request->setScoreDirty(); + } + } + std::vector mVolumes; +private: + std::weak_ptr mRequest; +}; + class LLMeshHeader { public: @@ -814,7 +864,7 @@ public: static void metricsProgress(unsigned int count); static void metricsUpdate(); - typedef std::unordered_map > mesh_load_map; + typedef std::unordered_map mesh_load_map; mesh_load_map mLoadingMeshes[4]; typedef std::unordered_map> skin_map; @@ -825,11 +875,11 @@ public: LLMutex* mMeshMutex; - typedef std::vector > pending_requests_vec; + typedef std::vector > pending_requests_vec; pending_requests_vec mPendingRequests; //list of mesh ids awaiting skin info - typedef std::unordered_map > skin_load_map; + typedef std::unordered_map skin_load_map; skin_load_map mLoadingSkins; //list of mesh ids awaiting decompositions -- cgit v1.2.3 From 6239647aa3623f84fa6c596638ecdb10b33e6b1b Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 23 Apr 2025 02:01:01 +0300 Subject: #3962 allow Tab work the same way as picking name with Enter --- indra/newview/llfloaterchatmentionpicker.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/newview') diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp index dda2cd83f6..1cfed122a9 100644 --- a/indra/newview/llfloaterchatmentionpicker.cpp +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -134,6 +134,7 @@ bool LLFloaterChatMentionPicker::handleKey(KEY key, MASK mask, bool called_from_ case KEY_DOWN: return mAvatarList->handleKey(key, mask, called_from_parent); case KEY_RETURN: + case KEY_TAB: selectResident(mAvatarList->getSelectedUUID()); return true; case KEY_ESCAPE: -- cgit v1.2.3 From 205b2282c7417d56433540fb4ee5df195545f9d2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 23 Apr 2025 18:09:11 +0300 Subject: #3920 LLPanel::getString crash Make sure new floater can launch wihtout a key. It isn't supposed to be launched wihtout a key, but there are some ways to do that intentionally. --- indra/newview/llfloaternewfeaturenotification.cpp | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp index 369727ff1e..1badcdd3d9 100644 --- a/indra/newview/llfloaternewfeaturenotification.cpp +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -43,12 +43,28 @@ bool LLFloaterNewFeatureNotification::postBuild() setCanDrag(false); getChild("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this)); - const std::string title_txt = "title_txt"; - const std::string dsc_txt = "description_txt"; - std::string feature = "_" + getKey().asString(); + if (getKey().isString()) + { + const std::string title_txt = "title_txt"; + const std::string dsc_txt = "description_txt"; - getChild(title_txt)->setValue(getString(title_txt + feature)); - getChild(dsc_txt)->setValue(getString(dsc_txt + feature)); + std::string feature = "_" + getKey().asString(); + if (hasString(title_txt + feature)) + { + getChild(title_txt)->setValue(getString(title_txt + feature)); + getChild(dsc_txt)->setValue(getString(dsc_txt + feature)); + } + else + { + // Show blank + LL_WARNS() << "Feature \"" << getKey().asString() << "\" not found for feature notification" << LL_ENDL; + } + } + else + { + // Show blank + LL_WARNS() << "Feature notification without a feature" << LL_ENDL; + } if (getKey().asString() == "gltf") { -- cgit v1.2.3 From b1b0cdbcf0c7a579bcce73296d4225499b544617 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 23 Apr 2025 19:07:44 +0300 Subject: #3784 Increase gltf scale boundaries --- indra/newview/skins/default/xui/en/panel_tools_texture.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml index af6a9b94d9..b7db9dec96 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -968,8 +968,8 @@ label_width="205" layout="topleft" left="10" - min_val="-100" - max_val="100" + min_val="-10000" + max_val="10000" name="gltfTextureScaleU" top_delta="34" width="265" /> @@ -981,8 +981,8 @@ label_width="205" layout="topleft" left="10" - min_val="-100" - max_val="100" + min_val="-10000" + max_val="10000" name="gltfTextureScaleV" width="265" /> Date: Wed, 23 Apr 2025 21:01:46 +0300 Subject: #3758 show mention name in bubble chat --- indra/newview/llviewerchat.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp index 8b01c4ef88..2ca2c5c07d 100644 --- a/indra/newview/llviewerchat.cpp +++ b/indra/newview/llviewerchat.cpp @@ -36,6 +36,7 @@ #include "llviewerregion.h" #include "llworld.h" #include "llinstantmessage.h" //SYSTEM_FROM +#include "llurlregistry.h" // LLViewerChat LLViewerChat::font_change_signal_t LLViewerChat::sChatFontChangedSignal; @@ -222,6 +223,13 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg) { std::string tmpmsg = chat.mText; + // show @name instead of slurl for chat mentions + LLUrlMatch match; + while (LLUrlRegistry::instance().findUrl(tmpmsg, match, LLUrlRegistryNullCallback, false, true)) + { + tmpmsg.replace(match.getStart(), match.getEnd() - match.getStart() + 1, match.getLabel()); + } + if(chat.mChatStyle == CHAT_STYLE_IRC) { formated_msg = chat.mFromName + tmpmsg.substr(3); -- cgit v1.2.3 From 89f817194305243cb28499c70819656b8172dfa8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 24 Apr 2025 20:42:27 +0300 Subject: #3975 Crash at LLSpatialGroup::dirtyGeom --- indra/newview/llvovolume.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 4e8932f912..63ff6bc79d 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -860,8 +860,11 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) // animated faces get moved to a smaller partition to reduce // side-effects of their updates (see shrinkWrap in // LLVOVolume::animateTextures). - mDrawable->getSpatialGroup()->dirtyGeom(); - gPipeline.markRebuild(mDrawable->getSpatialGroup()); + if (mDrawable->getSpatialGroup()) + { + mDrawable->getSpatialGroup()->dirtyGeom(); + gPipeline.markRebuild(mDrawable->getSpatialGroup()); + } } } -- cgit v1.2.3 From fb4925d96bd47136aa18fdb6d788236278fa29ec Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Fri, 25 Apr 2025 10:52:00 -0700 Subject: Increment viewer version after 2025.03 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 991d8e5c5f..099f298456 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.13 +7.1.14 -- cgit v1.2.3 From 69a81d641abb7427d22a4bfd6014b696476dd8b7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 25 Apr 2025 21:15:49 +0300 Subject: #3982 Crash at getChild Notification about inventory change from fetchInventoryFromCapCoro Looks like floater was closed a moment before receiving inventoryChanged --- indra/newview/llfloaterbulkpermission.cpp | 16 +++++++++++----- indra/newview/llfloaterbulkpermission.h | 7 ++++--- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index c09c02d32b..74c5079268 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -89,9 +89,17 @@ bool LLFloaterBulkPermission::postBuild() { mBulkChangeNextOwnerTransfer = true; } + + mQueueOutputList = getChild("queue output"); return true; } +void LLFloaterBulkPermission::onClose(bool app_quitting) +{ + removeVOInventoryListener(); + LLFloater::onClose(app_quitting); +} + void LLFloaterBulkPermission::doApply() { // Inspects a stream of selected object contents and adds modifiable ones to the given array. @@ -216,7 +224,7 @@ void LLFloaterBulkPermission::onCommitCopy() bool LLFloaterBulkPermission::start() { // note: number of top-level objects to modify is mObjectIDs.size(). - getChild("queue output")->setCommentText(getString("start_text")); + mQueueOutputList->setCommentText(getString("start_text")); return nextObject(); } @@ -239,7 +247,7 @@ bool LLFloaterBulkPermission::nextObject() if(isDone() && !mDone) { - getChild("queue output")->setCommentText(getString("done_text")); + mQueueOutputList->setCommentText(getString("done_text")); mDone = true; } return successful_start; @@ -294,8 +302,6 @@ void LLFloaterBulkPermission::doCheckUncheckAll(bool check) void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInventoryObject::object_list_t* inv) { - LLScrollListCtrl* list = getChild("queue output"); - LLInventoryObject::object_list_t::const_iterator it = inv->begin(); LLInventoryObject::object_list_t::const_iterator end = inv->end(); for ( ; it != end; ++it) @@ -362,7 +368,7 @@ void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInve status_text.setArg("[STATUS]", ""); } - list->setCommentText(status_text.getString()); + mQueueOutputList->setCommentText(status_text.getString()); //TODO if we are an object inside an object we should check a recuse flag and if set //open the inventory of the object and recurse - Michelle2 Zenovka diff --git a/indra/newview/llfloaterbulkpermission.h b/indra/newview/llfloaterbulkpermission.h index 23ca45b611..0b61022e0c 100644 --- a/indra/newview/llfloaterbulkpermission.h +++ b/indra/newview/llfloaterbulkpermission.h @@ -41,7 +41,8 @@ class LLFloaterBulkPermission : public LLFloater, public LLVOInventoryListener friend class LLFloaterReg; public: - bool postBuild(); + bool postBuild() override; + void onClose(bool app_quitting) override; private: @@ -57,7 +58,7 @@ private: /*virtual*/ void inventoryChanged(LLViewerObject* obj, LLInventoryObject::object_list_t* inv, S32 serial_num, - void* queue); + void* queue) override; // This is called by inventoryChanged void handleInventory(LLViewerObject* viewer_obj, @@ -85,7 +86,7 @@ private: private: // UI - LLScrollListCtrl* mMessages; + LLScrollListCtrl* mQueueOutputList = nullptr; LLButton* mCloseBtn; // Object Queue -- cgit v1.2.3 From 04c0bc100401c8f4541b6eeda3a110291ee54166 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 28 Apr 2025 17:17:29 +0300 Subject: #3978 Fix offset in Land Owner highlights texture_stride with '-1' was added in DRTVWR-592 along with getMetersPerGrid multiplication. --- indra/newview/llsurfacepatch.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index 6f99da5957..875af76c10 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -210,7 +210,6 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 llassert_always(vertex && normal && tex1); U32 surface_stride = mSurfacep->getGridsPerEdge(); - U32 texture_stride = mSurfacep->getGridsPerEdge() - 1; U32 point_offset = x + y*surface_stride; *normal = getNormal(x, y); @@ -223,7 +222,7 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 // tex0 is used for ownership overlay LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent(); - LLVector3 tex_pos = rel_pos * (1.f / (texture_stride * mSurfacep->getMetersPerGrid())); + LLVector3 tex_pos = rel_pos * (1.f / (surface_stride * mSurfacep->getMetersPerGrid())); tex0->mV[0] = tex_pos.mV[0]; tex0->mV[1] = tex_pos.mV[1]; -- cgit v1.2.3 From 7bd18e256d70804f72d747ccc1e62c92679dd645 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 25 Apr 2025 18:57:58 +0300 Subject: viewerp#300 Fix inconsistency with copying textures vs pbr --- indra/newview/llinventorymodel.cpp | 1 + indra/newview/llpanelface.cpp | 462 ++++++++++++++++++++++++------------- indra/newview/llpanelface.h | 3 + indra/newview/lltexturectrl.cpp | 3 +- 4 files changed, 312 insertions(+), 157 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 0a2d938bd0..cf1efd283a 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2768,6 +2768,7 @@ bool LLInventoryModel::loadSkeleton( bool is_cache_obsolete = false; if (loadFromFile(inventory_filename, categories, items, categories_to_update, is_cache_obsolete)) { + LL_PROFILE_ZONE_NAMED("loadFromFile"); // We were able to find a cache of files. So, use what we // found to generate a set of categories we should add. We // will go through each category loaded and if the version diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 6e0b0d252e..2e57e2a5bd 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -4009,6 +4009,85 @@ void LLPanelFace::onPasteColor(LLViewerObject* objectp, S32 te) } } +void set_item_availability( + const LLUUID& id, + LLSD& dest, + const std::string& modifier, + bool is_creator, + std::map &asset_item_map, + LLViewerObject* objectp) +{ + if (id.isNull()) + { + return; + } + + LLUUID item_id; + bool from_library = get_is_predefined_texture(id); + bool full_perm = from_library; + full_perm |= is_creator; + + if (!full_perm) + { + std::map::iterator iter = asset_item_map.find(id); + if (iter != asset_item_map.end()) + { + item_id = iter->second; + } + else + { + // What this does is simply searches inventory for item with same asset id, + // as result it is Hightly unreliable, leaves little control to user, borderline hack + // but there are little options to preserve permissions - multiple inventory + // items might reference same asset and inventory search is expensive. + bool no_transfer = false; + if (objectp->getInventoryItemByAsset(id)) + { + no_transfer = !objectp->getInventoryItemByAsset(id)->getIsFullPerm(); + } + item_id = get_copy_free_item_by_asset_id(id, no_transfer); + // record value to avoid repeating inventory search when possible + asset_item_map[id] = item_id; + } + } + + if (item_id.notNull() && gInventory.isObjectDescendentOf(item_id, gInventory.getLibraryRootFolderID())) + { + full_perm = true; + from_library = true; + } + + dest[modifier + "itemfullperm"] = full_perm; + dest[modifier + "fromlibrary"] = from_library; + + // If full permission object, texture is free to copy, + // but otherwise we need to check inventory and extract permissions + // + // Normally we care only about restrictions for current user and objects + // don't inherit any 'next owner' permissions from texture, so there is + // no need to record item id if full_perm==true + if (!full_perm && item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (itemp) + { + LLPermissions item_permissions = itemp->getPermissions(); + if (item_permissions.allowOperationBy(PERM_COPY, + gAgent.getID(), + gAgent.getGroupID())) + { + dest[modifier + "itemid"] = item_id; + dest[modifier + "itemfullperm"] = itemp->getIsFullPerm(); + if (!itemp->isFinished()) + { + // needed for dropTextureAllFaces + LLInventoryModelBackgroundFetch::instance().start(item_id, false); + } + } + } + } +} + void LLPanelFace::onCopyTexture() { LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); @@ -4046,6 +4125,7 @@ void LLPanelFace::onCopyTexture() if (tep) { LLSD te_data; + LLUUID pbr_id = objectp->getRenderMaterialID(te); // asLLSD() includes media te_data["te"] = tep->asLLSD(); @@ -4054,21 +4134,20 @@ void LLPanelFace::onCopyTexture() te_data["te"]["bumpshiny"] = tep->getBumpShiny(); te_data["te"]["bumpfullbright"] = tep->getBumpShinyFullbright(); te_data["te"]["texgen"] = tep->getTexGen(); - te_data["te"]["pbr"] = objectp->getRenderMaterialID(te); + te_data["te"]["pbr"] = pbr_id; if (tep->getGLTFMaterialOverride() != nullptr) { te_data["te"]["pbr_override"] = tep->getGLTFMaterialOverride()->asJSON(); } - if (te_data["te"].has("imageid")) + if (te_data["te"].has("imageid") || pbr_id.notNull()) { - LLUUID item_id; - LLUUID id = te_data["te"]["imageid"].asUUID(); - bool from_library = get_is_predefined_texture(id); - bool full_perm = from_library; + LLUUID img_id = te_data["te"]["imageid"].asUUID(); + bool pbr_from_library = false; + bool pbr_full_perm = false; + bool is_creator = false; - if (!full_perm - && objectp->permCopy() + if (objectp->permCopy() && objectp->permTransfer() && objectp->permModify()) { @@ -4078,66 +4157,31 @@ void LLPanelFace::onCopyTexture() std::string creator_app_link; LLUUID creator_id; LLSelectMgr::getInstance()->selectGetCreator(creator_id, creator_app_link); - full_perm = objectp->mOwnerID == creator_id; + is_creator = objectp->mOwnerID == creator_id; } - if (id.notNull() && !full_perm) + // check permissions for blin-phong/diffuse image and for pbr asset + if (img_id.notNull()) { - std::map::iterator iter = asset_item_map.find(id); - if (iter != asset_item_map.end()) - { - item_id = iter->second; - } - else - { - // What this does is simply searches inventory for item with same asset id, - // as result it is Hightly unreliable, leaves little control to user, borderline hack - // but there are little options to preserve permissions - multiple inventory - // items might reference same asset and inventory search is expensive. - bool no_transfer = false; - if (objectp->getInventoryItemByAsset(id)) - { - no_transfer = !objectp->getInventoryItemByAsset(id)->getIsFullPerm(); - } - item_id = get_copy_free_item_by_asset_id(id, no_transfer); - // record value to avoid repeating inventory search when possible - asset_item_map[id] = item_id; - } + set_item_availability(img_id, te_data["te"], "img", is_creator, asset_item_map, objectp); } - - if (item_id.notNull() && gInventory.isObjectDescendentOf(item_id, gInventory.getLibraryRootFolderID())) + if (pbr_id.notNull()) { - full_perm = true; - from_library = true; - } + set_item_availability(pbr_id, te_data["te"], "pbr", is_creator, asset_item_map, objectp); - { - te_data["te"]["itemfullperm"] = full_perm; - te_data["te"]["fromlibrary"] = from_library; - - // If full permission object, texture is free to copy, - // but otherwise we need to check inventory and extract permissions - // - // Normally we care only about restrictions for current user and objects - // don't inherit any 'next owner' permissions from texture, so there is - // no need to record item id if full_perm==true - if (!full_perm && !from_library && item_id.notNull()) + // permissions for overrides + // Overrides do not permit no-copy textures + LLGLTFMaterial* override = tep->getGLTFMaterialOverride(); + if (override != nullptr) { - LLViewerInventoryItem* itemp = gInventory.getItem(item_id); - if (itemp) + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) { - LLPermissions item_permissions = itemp->getPermissions(); - if (item_permissions.allowOperationBy(PERM_COPY, - gAgent.getID(), - gAgent.getGroupID())) + LLUUID& texture_id = override->mTextureId[i]; + if (texture_id.notNull()) { - te_data["te"]["imageitemid"] = item_id; - te_data["te"]["itemfullperm"] = itemp->getIsFullPerm(); - if (!itemp->isFinished()) - { - // needed for dropTextureAllFaces - LLInventoryModelBackgroundFetch::instance().start(item_id, false); - } + const std::string prefix = "pbr" + std::to_string(i); + te_data["te"][prefix + "imageid"] = texture_id; + set_item_availability(texture_id, te_data["te"], prefix, is_creator, asset_item_map, objectp); } } } @@ -4201,6 +4245,44 @@ void LLPanelFace::onCopyTexture() } } +bool get_full_permission(const LLSD& te, const std::string &prefix) +{ + return te.has(prefix + "itemfullperm") && te[prefix+"itemfullperm"].asBoolean(); +} + +bool LLPanelFace::validateInventoryItem(const LLSD& te, const std::string& prefix) +{ + if (te.has(prefix + "itemid")) + { + LLUUID item_id = te[prefix + "itemid"].asUUID(); + if (item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (!itemp) + { + // image might be in object's inventory, but it can be not up to date + LLSD notif_args; + static std::string reason = getString("paste_error_inventory_not_found"); + notif_args["REASON"] = reason; + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return false; + } + } + } + else + { + // Item was not found on 'copy' stage + // Since this happened at copy, might be better to either show this + // at copy stage or to drop clipboard here + LLSD notif_args; + static std::string reason = getString("paste_error_inventory_not_found"); + notif_args["REASON"] = reason; + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return false; + } + return true; +} + void LLPanelFace::onPasteTexture() { if (!mClipboardParams.has("texture")) @@ -4265,39 +4347,49 @@ void LLPanelFace::onPasteTexture() for (; iter != end; ++iter) { const LLSD& te_data = *iter; - if (te_data.has("te") && te_data["te"].has("imageid")) + if (te_data.has("te")) { - bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); - full_perm_object &= full_perm; - if (!full_perm) + if (te_data["te"].has("imageid")) { - if (te_data["te"].has("imageitemid")) + bool full_perm = get_full_permission(te_data["te"], "img"); + full_perm_object &= full_perm; + if (!full_perm) { - LLUUID item_id = te_data["te"]["imageitemid"].asUUID(); - if (item_id.notNull()) + if (!validateInventoryItem(te_data["te"], "img")) { - LLViewerInventoryItem* itemp = gInventory.getItem(item_id); - if (!itemp) - { - // image might be in object's inventory, but it can be not up to date - LLSD notif_args; - static std::string reason = getString("paste_error_inventory_not_found"); - notif_args["REASON"] = reason; - LLNotificationsUtil::add("FacePasteFailed", notif_args); - return; - } + return; } } - else + } + if (te_data["te"].has("pbr")) + { + bool full_perm = get_full_permission(te_data["te"], "pbr"); + full_perm_object &= full_perm; + if (!full_perm) { - // Item was not found on 'copy' stage - // Since this happened at copy, might be better to either show this - // at copy stage or to drop clipboard here - LLSD notif_args; - static std::string reason = getString("paste_error_inventory_not_found"); - notif_args["REASON"] = reason; - LLNotificationsUtil::add("FacePasteFailed", notif_args); - return; + if (!validateInventoryItem(te_data["te"], "pbr")) + { + return; + } + } + if (te_data["te"].has("pbr_override")) + { + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + const std::string prefix = "pbr" + std::to_string(i); + if (te_data["te"].has(prefix + "imageid")) + { + bool full_perm = get_full_permission(te_data["te"], prefix); + full_perm_object &= full_perm; + if (!full_perm) + { + if (!validateInventoryItem(te_data["te"], prefix)) + { + return; + } + } + } + } } } } @@ -4322,6 +4414,71 @@ void LLPanelFace::onPasteTexture() selected_objects->applyToTEs(&navigate_home_func); } +void get_item_and_permissions(const LLUUID &id, LLViewerInventoryItem*& itemp, bool& full_perm, bool& from_library, const LLSD &data, const std::string &prefix) +{ + full_perm = get_full_permission(data, prefix); + from_library = data.has(prefix + "fromlibrary") && data.get(prefix + "fromlibrary").asBoolean(); + LLViewerInventoryItem* itemp_res = NULL; + + if (data.has(prefix + "itemid")) + { + LLUUID item_id = data.get(prefix + "itemid").asUUID(); + if (item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (itemp && itemp->isFinished()) + { + // dropTextureAllFaces will fail if incomplete + itemp_res = itemp; + } + else + { + // Theoretically shouldn't happend, but if it does happen, we + // might need to add a notification to user that paste will fail + // since inventory isn't fully loaded + LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL; + } + } + } + + // for case when item got removed from inventory after we pressed 'copy' + // or texture got pasted into previous object + if (!itemp_res && !full_perm) + { + // Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable. + LL_INFOS() << "Item " << data.get(prefix + "itemid").asUUID() << " no longer in inventory." << LL_ENDL; + // Todo: fix this, we are often searching same texture multiple times (equal to number of faces) + // Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work + LLViewerInventoryCategory::cat_array_t cats; + LLViewerInventoryItem::item_array_t items; + LLAssetIDMatches asset_id_matches(id); + gInventory.collectDescendentsIf(LLUUID::null, + cats, + items, + LLInventoryModel::INCLUDE_TRASH, + asset_id_matches); + + // Extremely unreliable and perfomance unfriendly. + // But we need this to check permissions and it is how texture control finds items + for (S32 i = 0; i < items.size(); i++) + { + LLViewerInventoryItem* itemp = items[i]; + if (itemp && itemp->isFinished()) + { + // dropTextureAllFaces will fail if incomplete + LLPermissions item_permissions = itemp->getPermissions(); + if (item_permissions.allowOperationBy(PERM_COPY, + gAgent.getID(), + gAgent.getGroupID())) + { + itemp_res = itemp; + break; // first match + } + } + } + } +} + void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te) { LLSD te_data; @@ -4345,77 +4502,22 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te) if (te_data.has("te")) { // Texture - bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); - bool from_library = te_data["te"].has("fromlibrary") && te_data["te"]["fromlibrary"].asBoolean(); if (te_data["te"].has("imageid")) { + bool img_full_perm = false; + bool img_from_library = false; const LLUUID& imageid = te_data["te"]["imageid"].asUUID(); //texture or asset id - LLViewerInventoryItem* itemp_res = NULL; + LLViewerInventoryItem* img_itemp_res = NULL; - if (te_data["te"].has("imageitemid")) - { - LLUUID item_id = te_data["te"]["imageitemid"].asUUID(); - if (item_id.notNull()) - { - LLViewerInventoryItem* itemp = gInventory.getItem(item_id); - if (itemp && itemp->isFinished()) - { - // dropTextureAllFaces will fail if incomplete - itemp_res = itemp; - } - else - { - // Theoretically shouldn't happend, but if it does happen, we - // might need to add a notification to user that paste will fail - // since inventory isn't fully loaded - LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL; - } - } - } - // for case when item got removed from inventory after we pressed 'copy' - // or texture got pasted into previous object - if (!itemp_res && !full_perm) - { - // Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable. - LL_INFOS() << "Item " << te_data["te"]["imageitemid"].asUUID() << " no longer in inventory." << LL_ENDL; - // Todo: fix this, we are often searching same texture multiple times (equal to number of faces) - // Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work - LLViewerInventoryCategory::cat_array_t cats; - LLViewerInventoryItem::item_array_t items; - LLAssetIDMatches asset_id_matches(imageid); - gInventory.collectDescendentsIf(LLUUID::null, - cats, - items, - LLInventoryModel::INCLUDE_TRASH, - asset_id_matches); - - // Extremely unreliable and perfomance unfriendly. - // But we need this to check permissions and it is how texture control finds items - for (S32 i = 0; i < items.size(); i++) - { - LLViewerInventoryItem* itemp = items[i]; - if (itemp && itemp->isFinished()) - { - // dropTextureAllFaces will fail if incomplete - LLPermissions item_permissions = itemp->getPermissions(); - if (item_permissions.allowOperationBy(PERM_COPY, - gAgent.getID(), - gAgent.getGroupID())) - { - itemp_res = itemp; - break; // first match - } - } - } - } + get_item_and_permissions(imageid, img_itemp_res, img_full_perm, img_from_library, te_data["te"], "img"); - if (itemp_res) + if (img_itemp_res) { if (te == -1) // all faces { LLToolDragAndDrop::dropTextureAllFaces(objectp, - itemp_res, - from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, + img_itemp_res, + img_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null, false); } @@ -4423,15 +4525,15 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te) { LLToolDragAndDrop::dropTextureOneFace(objectp, te, - itemp_res, - from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, + img_itemp_res, + img_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null, false, 0); } } // not an inventory item or no complete items - else if (full_perm) + else if (img_full_perm) { // Either library, local or existed as fullperm when user made a copy LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(imageid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); @@ -4459,17 +4561,65 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te) // PBR/GLTF if (te_data["te"].has("pbr")) { - objectp->setRenderMaterialID(te, te_data["te"]["pbr"].asUUID(), false /*managing our own update*/); - tep->setGLTFRenderMaterial(nullptr); - tep->setGLTFMaterialOverride(nullptr); + const LLUUID pbr_id = te_data["te"]["pbr"].asUUID(); + bool pbr_full_perm = false; + bool pbr_from_library = false; + LLViewerInventoryItem* pbr_itemp_res = NULL; + + get_item_and_permissions(pbr_id, pbr_itemp_res, pbr_full_perm, pbr_from_library, te_data["te"], "pbr"); + bool allow = true; + + // check overrides first since they don't need t be moved to inventory if (te_data["te"].has("pbr_override")) { - LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID(), te_data["te"]["pbr_override"]); + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + const std::string prefix = "pbr" + std::to_string(i); + if (te_data["te"].has(prefix + "imageid")) + { + LLUUID tex_id = te_data["te"][prefix + "imageid"]; + + bool full_perm = false; + bool from_library = false; + LLViewerInventoryItem* itemp_res = NULL; + get_item_and_permissions(tex_id, itemp_res, full_perm, from_library, te_data["te"], prefix); + allow = full_perm; + if (!allow) break; + } + } + } + + if (allow && pbr_itemp_res) + { + if (pbr_itemp_res) + { + allow = LLToolDragAndDrop::handleDropMaterialProtections( + objectp, + pbr_itemp_res, + pbr_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, + pbr_id); + } + else + { + allow = pbr_full_perm; + } } - else + + if (allow) { - LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID()); + objectp->setRenderMaterialID(te, te_data["te"]["pbr"].asUUID(), false /*managing our own update*/); + tep->setGLTFRenderMaterial(nullptr); + tep->setGLTFMaterialOverride(nullptr); + + if (te_data["te"].has("pbr_override")) + { + LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID(), te_data["te"]["pbr_override"]); + } + else + { + LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID()); + } } } else diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 6e0d34cbd6..b59f7434af 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -264,6 +264,9 @@ public: // needs to be accessible to selection manager void onCopyTexture(); void onPasteTexture(); void onPasteTexture(LLViewerObject* objectp, S32 te); +private: + // for copy/paste operations + bool validateInventoryItem(const LLSD& te, const std::string& prefix); protected: void menuDoToSelected(const LLSD& userdata); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 35057a910a..20127f5f27 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -88,7 +88,8 @@ bool get_is_predefined_texture(LLUUID asset_id) || asset_id == DEFAULT_OBJECT_NORMAL || asset_id == BLANK_OBJECT_NORMAL || asset_id == IMG_WHITE - || asset_id == LLUUID(SCULPT_DEFAULT_TEXTURE)) + || asset_id == LLUUID(SCULPT_DEFAULT_TEXTURE) + || asset_id == BLANK_MATERIAL_ASSET_ID) { return true; } -- cgit v1.2.3 From fdda524ee1395c7dd5d5f65ce1f61c64e256c531 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 29 Apr 2025 00:50:38 +0300 Subject: #3791 check against alpha blending as a part of determining exclude water --- indra/newview/llpanelface.cpp | 111 ++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 54 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 2e57e2a5bd..99471a2555 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1106,6 +1106,63 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) updateVisibility(objectp); + bool missing_asset = false; + { + LLGLenum image_format = GL_RGB; + bool identical_image_format = false; + LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset); + + if (!missing_asset) + { + mIsAlpha = false; + switch (image_format) + { + case GL_RGBA: + case GL_ALPHA: + { + mIsAlpha = true; + } + break; + + case GL_RGB: + break; + default: + { + LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; + } + break; + } + } + else + { + // Don't know image's properties, use material's mode value + mIsAlpha = true; + } + + // Diffuse Alpha Mode + // Init to the default that is appropriate for the alpha content of the asset + // + U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; + + bool identical_alpha_mode = false; + + // See if that's been overridden by a material setting for same... + // + LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha); + + // it is invalid to have any alpha mode other than blend if transparency is greater than zero ... + // Want masking? Want emissive? Tough! You get BLEND! + alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode; + + // ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none + alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; + + mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode); + updateAlphaControls(); + + mExcludeWater &= (LLMaterial::DIFFUSE_ALPHA_MODE_BLEND == alpha_mode); + } + // Water exclusion { mCheckHideWater->setEnabled(editable && !has_pbr_material && !isMediaTexSelected()); @@ -1188,65 +1245,11 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) // Texture { - LLGLenum image_format = GL_RGB; - bool identical_image_format = false; - bool missing_asset = false; - LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset); - - if (!missing_asset) - { - mIsAlpha = false; - switch (image_format) - { - case GL_RGBA: - case GL_ALPHA: - { - mIsAlpha = true; - } - break; - - case GL_RGB: break; - default: - { - LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; - } - break; - } - } - else - { - // Don't know image's properties, use material's mode value - mIsAlpha = true; - } - if (LLViewerMedia::getInstance()->textureHasMedia(id)) { mBtnAlign->setEnabled(editable); } - // Diffuse Alpha Mode - - // Init to the default that is appropriate for the alpha content of the asset - // - U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; - - bool identical_alpha_mode = false; - - // See if that's been overridden by a material setting for same... - // - LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha); - - // it is invalid to have any alpha mode other than blend if transparency is greater than zero ... - // Want masking? Want emissive? Tough! You get BLEND! - alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode; - - // ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none - alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; - - mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode); - - updateAlphaControls(); - if (mTextureCtrl) { if (identical_diffuse) -- cgit v1.2.3 From d9e55c44152064133796bfb08f1da524387c1300 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 29 Apr 2025 17:09:19 +0300 Subject: #3997 Crash in a gltf asset enabled region --- indra/newview/gltfscenemanager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index e0cd762776..9faead9533 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -643,6 +643,12 @@ void GLTFSceneManager::render(Asset& asset, U8 variant) return; } + if (gGLTFPBRMetallicRoughnessProgram.mGLTFVariants.size() <= variant) + { + llassert(false); // mGLTFVariants should have been initialized + return; + } + for (U32 ds = 0; ds < 2; ++ds) { RenderData& rd = asset.mRenderData[ds]; -- cgit v1.2.3