diff options
Diffstat (limited to 'indra/newview')
34 files changed, 917 insertions, 372 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 77d9171f3f..473ca60728 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -212,6 +212,7 @@ set(viewer_SOURCE_FILES llfloatercamera.cpp llfloatercamerapresets.cpp llfloaterchangeitemthumbnail.cpp + llfloaterchatmentionpicker.cpp llfloaterchatvoicevolume.cpp llfloaterclassified.cpp llfloatercolorpicker.cpp @@ -887,6 +888,7 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamerapresets.h llfloaterchangeitemthumbnail.h + llfloaterchatmentionpicker.h llfloatercamera.h llfloaterchatvoicevolume.h llfloaterclassified.h 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 diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index bf3fada3bd..9a381f9ba6 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]; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 87b165b739..3cc84666ec 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -485,7 +485,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/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 52cd86951d..5f9d03ca66 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -147,6 +147,7 @@ LLAvatarList::LLAvatarList(const Params& p) , mShowSpeakingIndicator(p.show_speaking_indicator) , mShowPermissions(p.show_permissions_granted) , mShowCompleteName(false) +, mForceCompleteName(false) { setCommitOnSelectionChange(true); @@ -183,7 +184,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 @@ -381,7 +382,7 @@ void LLAvatarList::updateAvatarNames() for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++) { LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it); - item->setShowCompleteName(mShowCompleteName); + item->setShowCompleteName(mShowCompleteName, mForceCompleteName); item->updateAvatarName(); } mNeedUpdateNames = false; @@ -421,6 +422,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) { @@ -435,7 +441,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); @@ -451,6 +457,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); } @@ -609,6 +616,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<const LLAvatarListItem*>(item1); diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 37ad578a20..97b4f05985 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -98,11 +98,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(); @@ -117,6 +119,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: @@ -133,6 +136,7 @@ private: bool mShowSpeakingIndicator; bool mShowPermissions; bool mShowCompleteName; + bool mForceCompleteName; LLTimer* mLITUpdateTimer; // last interaction time update timer std::string mIconParamName; @@ -144,6 +148,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 7be4f4eeb8..1db7d63309 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -80,6 +80,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) mShowProfileBtn(true), mShowPermissions(false), mShowCompleteName(false), + mForceCompleteName(false), mHovered(false), mAvatarNameCacheConnection(), mGreyOutUsername("") @@ -350,13 +351,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) @@ -443,8 +442,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 630a7ec751..f9381f95e3 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -110,7 +110,7 @@ public: void showAvatarDistance(bool 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; @@ -228,6 +228,7 @@ private: bool mHovered; bool mShowCompleteName; + bool mForceCompleteName; std::string mGreyOutUsername; void fetchAvatarName(); 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<LLScrollListCtrl>("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<LLScrollListCtrl>("queue output")->setCommentText(getString("start_text")); + mQueueOutputList->setCommentText(getString("start_text")); return nextObject(); } @@ -239,7 +247,7 @@ bool LLFloaterBulkPermission::nextObject() if(isDone() && !mDone) { - getChild<LLScrollListCtrl>("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<LLScrollListCtrl>("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 diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp new file mode 100644 index 0000000000..1cfed122a9 --- /dev/null +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -0,0 +1,184 @@ +/** + * @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<LLAvatarList>("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<LLAvatarListItem*>(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<LLParticipantList*>(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<LLConversationItem*>(*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: + case KEY_TAB: + 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<LLParticipantList*>(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<std::string> 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 553aa1ff4b..cd428c67ef 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/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<LLButton>("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<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature)); - getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature)); + std::string feature = "_" + getKey().asString(); + if (hasString(title_txt + feature)) + { + getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature)); + getChild<LLUICtrl>(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") { diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index de1c7a7ed8..c9e9d50e19 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/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 48c80842b9..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); @@ -1809,42 +1869,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; } @@ -2226,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 } } @@ -4218,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); } } @@ -4247,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<PendingRequestBase> request(new PendingRequestLOD(mesh_params, new_lod)); + mPendingRequests.emplace_back(request); + mLoadingMeshes[new_lod][mesh_id].initData(vobj, request); LLMeshRepository::sLODPending++; } } @@ -4315,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); @@ -4495,13 +4506,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; @@ -4548,61 +4566,25 @@ void LLMeshRepository::notifyLoadedMeshes() if (mPendingRequests.size() > push_count) { + 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<LLUUID, F32> 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<PendingRequestBase>& req_p : mPendingRequests) + // update "score" for pending requests + for (std::shared_ptr<PendingRequestBase>& req_p : mPendingRequests) { - req_p->setScore(score_map[req_p->getId()]); + req_p->checkScore(); } //sort by "score" 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<PendingRequestBase>& req_p = mPendingRequests.front(); + std::shared_ptr<PendingRequestBase>& 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<PendingRequestBase> 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<PendingRequestBase>& lhs, const std::unique_ptr<PendingRequestBase>& rhs) + bool operator()(const std::shared_ptr<PendingRequestBase>& lhs, const std::shared_ptr<PendingRequestBase>& 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<PendingRequestBase> request = mRequest.lock()) + { + request->untrackData(); + } + } + void initData(LLVOVolume* vol, std::shared_ptr<PendingRequestBase>& request) + { + mVolumes.push_back(vol); + request->trackData(this); + mRequest = request; + } + void addVolume(LLVOVolume* vol) + { + mVolumes.push_back(vol); + if (std::shared_ptr<PendingRequestBase> request = mRequest.lock()) + { + request->setScoreDirty(); + } + } + std::vector<LLVOVolume*> mVolumes; +private: + std::weak_ptr<PendingRequestBase> mRequest; +}; + class LLMeshHeader { public: @@ -814,7 +864,7 @@ public: static void metricsProgress(unsigned int count); static void metricsUpdate(); - typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map; + typedef std::unordered_map<LLUUID, MeshLoadData> mesh_load_map; mesh_load_map mLoadingMeshes[4]; typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map; @@ -825,11 +875,11 @@ public: LLMutex* mMeshMutex; - typedef std::vector <std::unique_ptr<PendingRequestBase> > pending_requests_vec; + typedef std::vector <std::shared_ptr<PendingRequestBase> > pending_requests_vec; pending_requests_vec mPendingRequests; //list of mesh ids awaiting skin info - typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map; + typedef std::unordered_map<LLUUID, MeshLoadData > skin_load_map; skin_load_map mLoadingSkins; //list of mesh ids awaiting decompositions diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 3a164ec94b..5b059516cd 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) @@ -4009,6 +4012,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<LLUUID, LLUUID> &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<LLUUID, LLUUID>::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 +4128,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 +4137,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 +4160,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<LLUUID, LLUUID>::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 +4248,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 +4350,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 +4417,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 +4505,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 +4528,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 +4564,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; + } + } } - else + + 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; + } + } + + 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 1ee9bf2cf7..ce3dd8bdea 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/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 132098ba99..1fa1c9587f 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 9a26ebc5f9..72295ffba9 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3601,7 +3601,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/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]; 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; } 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); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 7d138dfb3b..39c5b9f218 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" @@ -356,6 +357,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>); LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>); LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); + LLFloaterReg::add("chat_mention_picker", "floater_chat_mention_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatMentionPicker>); LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 3a52908b8a..a6d50af025 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -6418,6 +6418,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; } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 3fb7a3c156..6b3dccf89c 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()); + } } } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index bc9aef8974..c549a7dddb 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -1004,4 +1004,10 @@ <color name="OutfitSnapshotMacMask2" value="0.1 0.1 0.1 1"/> + <color + name="ChatMentionHighlight" + value="0.82 0.91 0.98 0.15" /> + <color + name="ChatSelfMentionHighlight" + value="0.85 0.75 0.40 0.5" /> </colors> 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + name="chat_mention_picker" + title="CHOOSE RESIDENT" + single_instance="true" + can_minimize="false" + can_tear_off="false" + can_resize="true" + auto_close="true" + layout="topleft" + min_width="250" + chrome="true" + height="125" + width="310"> + <avatar_list + allow_select="true" + follows="all" + height="120" + width="306" + ignore_online_status="true" + layout="topleft" + left="3" + keep_one_selected="true" + multi_select="false" + show_info_btn="false" + show_profile_btn="false" + show_speaking_indicator="false" + name="avatar_list" + right="-1" + top="2" /> +</floater> 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" /> <spinner |