diff options
31 files changed, 1053 insertions, 211 deletions
diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp index f6cb3c8b70..0d146de949 100644 --- a/indra/llprimitive/llmaterial.cpp +++ b/indra/llprimitive/llmaterial.cpp @@ -332,17 +332,6 @@ void LLMaterial::setAlphaMaskCutoff(U8 cutoff) mAlphaMaskCutoff = cutoff; } -LLUUID LLMaterial::getMaterialID() const -{ - // TODO - not null - return LLUUID::null; -} - -void LLMaterial::setMaterialID(const LLUUID &material_id) -{ - // TODO - set -} - LLSD LLMaterial::asLLSD() const { LLSD material_data; diff --git a/indra/llprimitive/llmaterial.h b/indra/llprimitive/llmaterial.h index b46d85c2d1..81f41ddc51 100644 --- a/indra/llprimitive/llmaterial.h +++ b/indra/llprimitive/llmaterial.h @@ -115,8 +115,6 @@ public: void setDiffuseAlphaMode(U8 alpha_mode); U8 getAlphaMaskCutoff() const; void setAlphaMaskCutoff(U8 cutoff); - LLUUID getMaterialID() const; - void setMaterialID(LLUUID const & material_id); bool isNull() const; static const LLMaterial null; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c6d82ea260..d413c12de9 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -246,6 +246,7 @@ set(viewer_SOURCE_FILES llfloatermyscripts.cpp llfloatermyenvironment.cpp llfloaternamedesc.cpp + llfloaternewfeaturenotification.cpp llfloaternotificationsconsole.cpp llfloaternotificationstabbed.cpp llfloateroutfitphotopreview.cpp @@ -895,6 +896,7 @@ set(viewer_HEADER_FILES llfloatermyscripts.h llfloatermyenvironment.h llfloaternamedesc.h + llfloaternewfeaturenotification.h llfloaternotificationsconsole.h llfloaternotificationstabbed.h llfloateroutfitphotopreview.h diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 2a26cb9a43..482012cdd6 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -73,7 +73,6 @@ <string>Avatar</string> <string>Voice</string> --> - <string>Capabilities</string> </array> </map> </array> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4612ee1a82..ee4c76943e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -250,7 +250,7 @@ <key>TextureLivePreview</key> <map> <key>Comment</key> - <string>Preview selections in texture picker immediately</string> + <string>Preview selections in texture picker or material picker immediately</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -5519,6 +5519,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>LastUIFeatureVersion</key> + <map> + <key>Comment</key> + <string>UI Feature Version number for tracking feature notification between viewer builds</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>LLSD</string> + <key>Value</key> + <string></string> + </map> <key>LastFindPanel</key> <map> <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ea176dd8de..be764287cc 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -120,6 +120,11 @@ const F64 CHAT_AGE_FAST_RATE = 3.0; const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds +const S32 UI_FEATURE_VERSION = 1; +// For version 1: 1 - inventory, 2 - gltf +// Will need to change to 3 once either inventory or gltf releases and cause a conflict +const S32 UI_FEATURE_FLAGS = 2; + // The agent instance. LLAgent gAgent; @@ -372,7 +377,7 @@ LLAgent::LLAgent() : mHideGroupTitle(FALSE), mGroupID(), - mInitialized(FALSE), + mInitialized(false), mListener(), mDoubleTapRunTimer(), @@ -447,7 +452,7 @@ LLAgent::LLAgent() : mNextFidgetTime(0.f), mCurrentFidget(0), - mFirstLogin(FALSE), + mFirstLogin(false), mOutfitChosen(FALSE), mVoiceConnected(false), @@ -504,7 +509,7 @@ void LLAgent::init() mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT); - mInitialized = TRUE; + mInitialized = true; } //----------------------------------------------------------------------------- @@ -559,6 +564,93 @@ void LLAgent::onAppFocusGained() } } +void LLAgent::setFirstLogin(bool b) +{ + mFirstLogin = b; + + if (mFirstLogin) + { + // Don't notify new users about new features + if (getFeatureVersion() <= UI_FEATURE_VERSION) + { + setFeatureVersion(UI_FEATURE_VERSION, UI_FEATURE_FLAGS); + } + } +} + +void LLAgent::setFeatureVersion(S32 version, S32 flags) +{ + LLSD updated_version; + updated_version["version"] = version; + updated_version["flags"] = flags; + gSavedSettings.setLLSD("LastUIFeatureVersion", updated_version); +} + +S32 LLAgent::getFeatureVersion() +{ + S32 version; + S32 flags; + getFeatureVersionAndFlags(version, flags); + return version; +} + +void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags) +{ + version = 0; + flags = 0; + LLSD feature_version = gSavedSettings.getLLSD("LastUIFeatureVersion"); + if (feature_version.isInteger()) + { + version = feature_version.asInteger(); + flags = 1; // inventory flag + } + else if (feature_version.isMap()) + { + version = feature_version["version"]; + flags = feature_version["flags"]; + } + else if (!feature_version.isString() && !feature_version.isUndefined()) + { + // is something newer inside? + version = UI_FEATURE_VERSION; + flags = UI_FEATURE_FLAGS; + } +} + +void LLAgent::showLatestFeatureNotification(const std::string key) +{ + S32 version; + S32 flags; // a single release can have multiple new features + getFeatureVersionAndFlags(version, flags); + if (version <= UI_FEATURE_VERSION && (flags & UI_FEATURE_FLAGS) != UI_FEATURE_FLAGS) + { + S32 flag = 0; + + if (key == "inventory") + { + // Notify user about new thumbnail support + flag = 1; + } + + if (key == "gltf") + { + flag = 2; + } + + if ((flags & flag) == 0) + { + // Need to open on top even if called from onOpen, + // do on idle to make sure it's on top + LLSD floater_key(key); + doOnIdleOneTime([floater_key]() + { + LLFloaterReg::showInstance("new_feature_notification", floater_key); + }); + + setFeatureVersion(UI_FEATURE_VERSION, flags | flag); + } + } +} void LLAgent::ageChat() { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 498bea3c07..0ce6fda131 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -117,15 +117,20 @@ private: //-------------------------------------------------------------------- public: void onAppFocusGained(); - void setFirstLogin(BOOL b) { mFirstLogin = b; } + void setFirstLogin(bool b); // Return TRUE if the database reported this login as the first for this particular user. - BOOL isFirstLogin() const { return mFirstLogin; } - BOOL isInitialized() const { return mInitialized; } + bool isFirstLogin() const { return mFirstLogin; } + bool isInitialized() const { return mInitialized; } + + void setFeatureVersion(S32 version, S32 flags); + S32 getFeatureVersion(); + void getFeatureVersionAndFlags(S32 &version, S32 &flags); + void showLatestFeatureNotification(const std::string key); public: std::string mMOTD; // Message of the day private: - BOOL mInitialized; - BOOL mFirstLogin; + bool mInitialized; + bool mFirstLogin; boost::shared_ptr<LLAgentListener> mListener; //-------------------------------------------------------------------- diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index a3cc939f85..abc9cdbcc2 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -76,6 +76,8 @@ BOOL LLFloaterBulkPermission::postBuild() mBulkChangeIncludeSounds = gSavedSettings.getBOOL("BulkChangeIncludeSounds"); mBulkChangeIncludeTextures = gSavedSettings.getBOOL("BulkChangeIncludeTextures"); mBulkChangeIncludeSettings = gSavedSettings.getBOOL("BulkChangeIncludeSettings"); + mBulkChangeIncludeMaterials = gSavedSettings.getBOOL("BulkChangeIncludeMaterials"); + mBulkChangeShareWithGroup = gSavedSettings.getBOOL("BulkChangeShareWithGroup"); mBulkChangeEveryoneCopy = gSavedSettings.getBOOL("BulkChangeEveryoneCopy"); mBulkChangeNextOwnerModify = gSavedSettings.getBOOL("BulkChangeNextOwnerModify"); @@ -188,6 +190,8 @@ void LLFloaterBulkPermission::onCloseBtn() gSavedSettings.setBOOL("BulkChangeIncludeSounds", mBulkChangeIncludeSounds); gSavedSettings.setBOOL("BulkChangeIncludeTextures", mBulkChangeIncludeTextures); gSavedSettings.setBOOL("BulkChangeIncludeSettings", mBulkChangeIncludeSettings); + gSavedSettings.setBOOL("BulkChangeIncludeMaterials", mBulkChangeIncludeMaterials); + gSavedSettings.setBOOL("BulkChangeShareWithGroup", mBulkChangeShareWithGroup); gSavedSettings.setBOOL("BulkChangeEveryoneCopy", mBulkChangeEveryoneCopy); gSavedSettings.setBOOL("BulkChangeNextOwnerModify", mBulkChangeNextOwnerModify); @@ -284,6 +288,7 @@ void LLFloaterBulkPermission::doCheckUncheckAll(BOOL check) gSavedSettings.setBOOL("BulkChangeIncludeSounds" , check); gSavedSettings.setBOOL("BulkChangeIncludeTextures" , check); gSavedSettings.setBOOL("BulkChangeIncludeSettings" , check); + gSavedSettings.setBOOL("BulkChangeIncludeMaterials" , check); } diff --git a/indra/newview/llfloaterbulkpermission.h b/indra/newview/llfloaterbulkpermission.h index 1afc876bba..ab5d568667 100644 --- a/indra/newview/llfloaterbulkpermission.h +++ b/indra/newview/llfloaterbulkpermission.h @@ -103,6 +103,8 @@ private: bool mBulkChangeIncludeSounds; bool mBulkChangeIncludeTextures; bool mBulkChangeIncludeSettings; + bool mBulkChangeIncludeMaterials; + bool mBulkChangeShareWithGroup; bool mBulkChangeEveryoneCopy; bool mBulkChangeNextOwnerModify; diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp new file mode 100644 index 0000000000..1e50024967 --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -0,0 +1,83 @@ +/** + * @file llfloaternewfeaturenotification.cpp + * @brief LLFloaterNewFeatureNotification class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llfloaternewfeaturenotification.h" + + +LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification() +{ +} + +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(); + + getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature)); + getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature)); + + if (getKey().asString() == "gltf") + { + LLRect rect = getRect(); + // make automatic? + reshape(rect.getWidth() + 90, rect.getHeight() + 45); + } + + return TRUE; +} + +void LLFloaterNewFeatureNotification::onOpen(const LLSD& key) +{ + centerOnScreen(); +} + +void LLFloaterNewFeatureNotification::onCloseBtn() +{ + closeFloater(); +} + +void LLFloaterNewFeatureNotification::centerOnScreen() +{ + LLVector2 window_size = LLUI::getInstance()->getWindowSize(); + centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); + LLFloaterView* parent = dynamic_cast<LLFloaterView*>(getParent()); + if (parent) + { + parent->bringToFront(this); + } +} + diff --git a/indra/newview/llfloaternewfeaturenotification.h b/indra/newview/llfloaternewfeaturenotification.h new file mode 100644 index 0000000000..95501451dc --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.h @@ -0,0 +1,49 @@ +/** + * @file llfloaternewfeaturenotification.h + * @brief LLFloaterNewFeatureNotification class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 LL_FLOATER_NEW_FEATURE_NOTOFICATION_H +#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H + +#include "llfloater.h" + +class LLFloaterNewFeatureNotification: + public LLFloater +{ + friend class LLFloaterReg; +public: + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + +private: + LLFloaterNewFeatureNotification(const LLSD& key); + /*virtual*/ ~LLFloaterNewFeatureNotification(); + + void centerOnScreen(); + + void onCloseBtn(); +}; + +#endif diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index 3a0e64985c..54d85c87ac 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -37,6 +37,7 @@ #include "llfilesystem.h" #include "llgltfmateriallist.h" #include "llinventorymodel.h" +#include "llinventoryobserver.h" #include "lllocalgltfmaterials.h" #include "llnotificationsutil.h" #include "lltexturectrl.h" @@ -237,7 +238,9 @@ struct LLSelectedTEGetMatData : public LLSelectedTEFunctor LLUUID mTexEmissiveId; LLUUID mTexNormalId; LLUUID mObjectId; + LLViewerObject* mObject = nullptr; S32 mObjectTE; + LLUUID mMaterialId; LLPointer<LLGLTFMaterial> mMaterial; LLPointer<LLLocalGLTFMaterial> mLocalMaterial; }; @@ -259,6 +262,7 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) return false; } LLUUID mat_id = objectp->getRenderMaterialID(te_index); + mMaterialId = mat_id; bool can_use = mIsOverride ? objectp->permModify() : objectp->permCopy(); LLTextureEntry *tep = objectp->getTE(te_index); // We might want to disable this entirely if at least @@ -290,6 +294,7 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) mTexEmissiveId = tex_emissive_id; mTexNormalId = tex_normal_id; mObjectTE = te_index; + mObject = objectp; mObjectId = objectp->getID(); mFirst = false; } @@ -318,6 +323,8 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) LLGLTFMaterial *mat = tep->getGLTFMaterial(); LLLocalGLTFMaterial *local_mat = dynamic_cast<LLLocalGLTFMaterial*>(mat); + mObject = objectp; + mObjectId = objectp->getID(); if (local_mat) { mLocalMaterial = local_mat; @@ -1192,7 +1199,9 @@ bool LLMaterialEditor::saveIfNeeded() { //make a new inventory item std::string res_desc = buildMaterialDescription(); - createInventoryItem(buffer, mMaterialName, res_desc); + LLPermissions local_permissions; + local_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + createInventoryItem(buffer, mMaterialName, res_desc, local_permissions); // We do not update floater with uploaded asset yet, so just close it. closeFloater(); @@ -1286,36 +1295,37 @@ bool LLMaterialEditor::updateInventoryItem(const std::string &buffer, const LLUU return true; } -void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc) +// Callback intended for when a material is saved from an object and needs to +// be modified to reflect the new asset/name. +class LLObjectsMaterialItemCallback : public LLInventoryCallback { - // gen a new uuid for this asset - LLTransactionID tid; - tid.generate(); // timestamp-based randomization + uniquification - U32 next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Materials"); - LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL); - const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ? +public: + LLObjectsMaterialItemCallback(const LLPermissions& permissions, const std::string& asset_data, const std::string& new_name) + : mPermissions(permissions), + mAssetData(asset_data), + mNewName(new_name) + { + } - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, name, desc, - LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, next_owner_perm, - new LLBoostFuncInventoryCallback([output = buffer](LLUUID const& inv_item_id) + void fire(const LLUUID& inv_item_id) override { LLViewerInventoryItem* item = gInventory.getItem(inv_item_id); - if (item) + if (!item) { - // create_inventory_item doesn't allow presetting some permissions, fix it now - LLPermissions perm = item->getPermissions(); - if (perm.getMaskEveryone() != LLFloaterPerms::getEveryonePerms("Materials") - || perm.getMaskGroup() != LLFloaterPerms::getGroupPerms("Materials")) - { - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Materials")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Materials")); + return; + } - item->setPermissions(perm); + // create_inventory_item/copy_inventory_item don't allow presetting some permissions, fix it now + item->setPermissions(mPermissions); + item->updateServer(FALSE); + gInventory.updateItem(item); + gInventory.notifyObservers(); - item->updateServer(FALSE); - gInventory.updateItem(item); - gInventory.notifyObservers(); - } + if (item->getName() != mNewName) + { + LLSD updates; + updates["name"] = mNewName; + update_inventory_item(inv_item_id, updates, NULL); } // from reference in LLSettingsVOBase::createInventoryItem()/updateInventoryItem() @@ -1323,7 +1333,7 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std: std::make_shared<LLBufferedAssetUploadInfo>( inv_item_id, LLAssetType::AT_MATERIAL, - output, + mAssetData, [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response) { // done callback @@ -1342,8 +1352,25 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std: } LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo); } - }) - ); + } +private: + LLPermissions mPermissions; + std::string mAssetData; + std::string mNewName; +}; + +void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions) +{ + // gen a new uuid for this asset + LLTransactionID tid; + tid.generate(); // timestamp-based randomization + uniquification + LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL); + const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ? + + LLPointer<LLObjectsMaterialItemCallback> cb = new LLObjectsMaterialItemCallback(permissions, buffer, name); + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, name, desc, + LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, permissions.getMaskNextOwner(), + cb); } void LLMaterialEditor::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId) @@ -1792,55 +1819,151 @@ void LLMaterialEditor::loadLive() } } -void LLMaterialEditor::saveObjectsMaterialAs() +// *NOTE: permissions_out includes user preferences for new item creation (LLFloaterPerms) +bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<PermissionBit>& ops, LLPermissions& permissions_out, LLViewerInventoryItem*& item_out) { - LLSelectedTEGetMatData func(false); + if (!LLMaterialEditor::capabilitiesAvailable()) + { + return false; + } + + // func.mIsOverride=true is used for the singleton material editor floater + // associated with the build floater. This flag also excludes objects from + // the selection that do not satisfy PERM_MODIFY. + llassert(func.mIsOverride); LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/); - saveMaterialAs(func.mMaterial, func.mLocalMaterial); -} -void LLMaterialEditor::savePickedMaterialAs() -{ - LLPickInfo pick = LLToolPie::getInstance()->getPick(); - if (pick.mPickType != LLPickInfo::PICK_OBJECT || !pick.getObject()) + + LLViewerObject* selected_object = func.mObject; + if (!selected_object) { - return; + // LLSelectedTEGetMatData can fail if there are no selected faces + // with materials, but we expect at least some object is selected. + llassert(LLSelectMgr::getInstance()->getSelection()->getFirstObject()); + return false; + } + if (selected_object->isInventoryPending()) + { + return false; + } + for (PermissionBit op : ops) + { + if (op == PERM_MODIFY && selected_object->isPermanentEnforced()) + { + return false; + } } - LLPointer<LLGLTFMaterial> render_material; - LLPointer<LLLocalGLTFMaterial> local_material; + item_out = selected_object->getInventoryItemByAsset(func.mMaterialId); - LLViewerObject *objectp = pick.getObject(); - LLUUID mat_id = objectp->getRenderMaterialID(pick.mObjectFace); - if (mat_id.notNull() && objectp->permCopy()) + LLPermissions item_permissions; + if (item_out) { - // Try a face user picked first - // (likely the only method we need, but in such case - // enable_object_save_gltf_material will need to check this) - LLTextureEntry *tep = objectp->getTE(pick.mObjectFace); - LLGLTFMaterial *mat = tep->getGLTFMaterial(); - LLLocalGLTFMaterial *local_mat = dynamic_cast<LLLocalGLTFMaterial*>(mat); + item_permissions.set(item_out->getPermissions()); + for (PermissionBit op : ops) + { + if (!gAgent.allowOperation(op, item_permissions, GP_OBJECT_MANIPULATE)) + { + return false; + } + } + // Update flags for new owner + if (!item_permissions.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true)) + { + llassert(false); + return false; + } + } + else + { + item_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + } - if (local_mat) + // Use root object for permissions checking + LLViewerObject* root_object = selected_object->getRootEdit(); + LLPermissions* object_permissions_p = LLSelectMgr::getInstance()->findObjectPermissions(root_object); + LLPermissions object_permissions; + if (object_permissions_p) + { + object_permissions.set(*object_permissions_p); + for (PermissionBit op : ops) { - local_material = local_mat; + if (!gAgent.allowOperation(op, object_permissions, GP_OBJECT_MANIPULATE)) + { + return false; + } } - render_material = tep->getGLTFRenderMaterial(); + // Update flags for new owner + if (!object_permissions.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true)) + { + llassert(false); + return false; + } + } + else + { + object_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + } + + LLPermissions floater_perm; + floater_perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + floater_perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Materials")); + floater_perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Materials")); + floater_perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Materials")); + + // *NOTE: A close inspection of LLPermissions::accumulate shows that + // conflicting UUIDs will be unset. This is acceptable behavior for now. + // The server will populate creator info based on the item creation method + // used. + // *NOTE: As far as I'm aware, there is currently no good way to preserve + // creation history when there's no material item present. In that case, + // the agent who saved the material will be considered the creator. + // -Cosmic,2023-08-07 + if (item_out) + { + permissions_out.set(item_permissions); } else { - // Find an applicable material. - // Do this before showing message, because - // message is going to drop selection. - LLSelectedTEGetMatData func(false); - LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/); - local_material = func.mLocalMaterial; - render_material = func.mMaterial; + permissions_out.set(object_permissions); } + permissions_out.accumulate(floater_perm); + + return true; +} + +bool LLMaterialEditor::canModifyObjectsMaterial() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item_out; + return can_use_objects_material(func, std::vector({PERM_MODIFY}), permissions, item_out); +} + +bool LLMaterialEditor::canSaveObjectsMaterial() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item_out; + return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item_out); +} - saveMaterialAs(render_material, local_material); +void LLMaterialEditor::saveObjectsMaterialAs() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item = nullptr; + bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item); + if (!allowed) + { + LL_WARNS("MaterialEditor") << "Failed to save GLTF material from object" << LL_ENDL; + return; + } + const LLUUID item_id = item ? item->getUUID() : LLUUID::null; + saveObjectsMaterialAs(func.mMaterial, func.mLocalMaterial, permissions, func.mObjectId, item_id); } -void LLMaterialEditor::saveMaterialAs(const LLGLTFMaterial* render_material, const LLLocalGLTFMaterial *local_material) + +void LLMaterialEditor::saveObjectsMaterialAs(const LLGLTFMaterial* render_material, const LLLocalGLTFMaterial *local_material, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id) { if (local_material) { @@ -1916,26 +2039,98 @@ void LLMaterialEditor::saveMaterialAs(const LLGLTFMaterial* render_material, con LLSD args; args["DESC"] = LLTrans::getString("New Material"); - LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2)); + if (local_material) + { + LLPermissions local_permissions; + local_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, local_permissions)); + } + else + { + if (item_id.notNull()) + { + // Copy existing item from object inventory, and create new composite asset on top of it + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback, _1, _2, permissions, object_id, item_id)); + } + else + { + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, permissions)); + } + } } -void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response) +// static +void LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) + if (0 != option) + { + return; + } + + LLSD asset; + asset["version"] = LLGLTFMaterial::ASSET_VERSION; + asset["type"] = LLGLTFMaterial::ASSET_TYPE; + // This is the string serialized from LLGLTFMaterial::asJSON + asset["data"] = notification["payload"]["data"]; + + std::ostringstream str; + LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); + + LLViewerObject* object = gObjectList.findObject(object_id); + if (!object) + { + return; + } + const LLInventoryItem* item = object->getInventoryItem(item_id); + if (!item) { - LLSD asset; - asset["version"] = LLGLTFMaterial::ASSET_VERSION; - asset["type"] = LLGLTFMaterial::ASSET_TYPE; - // This is the string serialized from LLGLTFMaterial::asJSON - asset["data"] = notification["payload"]["data"]; + return; + } - std::ostringstream str; - LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); + std::string new_name = response["message"].asString(); + LLInventoryObject::correctInventoryName(new_name); + if (new_name.empty()) + { + return; + } - std::string new_name = response["message"].asString(); - createInventoryItem(str.str(), new_name, std::string()); + const LLUUID destination_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL); + + LLPointer<LLInventoryCallback> cb = new LLObjectsMaterialItemCallback(permissions, str.str(), new_name); + // NOTE: This should be an item copy. Saving a material to an inventory should be disabled when the associated material is no-copy. + move_or_copy_inventory_from_object(destination_id, + object_id, + item_id, + cb); +} + +// static +void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 != option) + { + return; } + + LLSD asset; + asset["version"] = LLGLTFMaterial::ASSET_VERSION; + asset["type"] = LLGLTFMaterial::ASSET_TYPE; + // This is the string serialized from LLGLTFMaterial::asJSON + asset["data"] = notification["payload"]["data"]; + + std::ostringstream str; + LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); + + std::string new_name = response["message"].asString(); + LLInventoryObject::correctInventoryName(new_name); + if (new_name.empty()) + { + return; + } + + createInventoryItem(str.str(), new_name, std::string(), permissions); } const void upload_bulk(const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter type); @@ -2708,7 +2903,10 @@ bool LLMaterialEditor::setFromSelection() if (func.mMaterial.notNull()) { setFromGLTFMaterial(func.mMaterial); - setEnableEditing(true); + LLViewerObject* selected_object = func.mObject; + const LLViewerInventoryItem* item = selected_object->getInventoryItemByAsset(func.mMaterialId); + const bool allow_modify = !item || canModify(selected_object, item); + setEnableEditing(allow_modify); } else { diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h index 6f674a4170..6b2f49e2fc 100644 --- a/indra/newview/llmaterialeditor.h +++ b/indra/newview/llmaterialeditor.h @@ -38,6 +38,7 @@ class LLGLTFMaterial; class LLLocalGLTFMaterial; class LLTextureCtrl; class LLTextBox; +class LLViewerInventoryItem; namespace tinygltf { @@ -112,9 +113,11 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener static void updateLive(const LLUUID &object_id, S32 te); static void loadLive(); + static bool canModifyObjectsMaterial(); + static bool canSaveObjectsMaterial(); static void saveObjectsMaterialAs(); - static void savePickedMaterialAs(); - static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response); + static void onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id); + static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions); static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -229,10 +232,10 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener static bool capabilitiesAvailable(); private: - static void saveMaterialAs(const LLGLTFMaterial *render_material, const LLLocalGLTFMaterial *local_material); + static void saveObjectsMaterialAs(const LLGLTFMaterial *render_material, const LLLocalGLTFMaterial *local_material, const LLPermissions& permissions, const LLUUID& object_id /* = LLUUID::null */, const LLUUID& item /* = LLUUID::null */); static bool updateInventoryItem(const std::string &buffer, const LLUUID &item_id, const LLUUID &task_id); - static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc); + static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions); void setFromGLTFMaterial(LLGLTFMaterial* mat); bool setFromSelection(); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 83a330af37..b502fa3546 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -51,6 +51,7 @@ #include "llinventorymodelbackgroundfetch.h" #include "llfloatermediasettings.h" #include "llfloaterreg.h" +#include "llfloatertools.h" #include "lllineeditor.h" #include "llmaterialmgr.h" #include "llmaterialeditor.h" @@ -77,6 +78,7 @@ #include "llviewerregion.h" #include "llviewerstats.h" #include "llvovolume.h" +#include "llvoinventorylistener.h" #include "lluictrlfactory.h" #include "llpluginclassmedia.h" #include "llviewertexturelist.h"// Update sel manager as to which channel we're editing so it can reflect the correct overlay UI @@ -490,6 +492,15 @@ LLPanelFace::~LLPanelFace() unloadMedia(); } +void LLPanelFace::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility) + { + gAgent.showLatestFeatureNotification("gltf"); + } + LLPanel::onVisibilityChange(new_visibility); +} + void LLPanelFace::draw() { updateCopyTexButton(); @@ -991,7 +1002,8 @@ void LLPanelFace::getState() void LLPanelFace::updateUI(bool force_set_values /*false*/) { //set state of UI to match state of texture entry(ies) (calls setEnabled, setValue, etc, but NOT setVisible) - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + LLViewerObject* objectp = node ? node->getObject() : NULL; if (objectp && objectp->getPCode() == LL_PCODE_VOLUME @@ -1022,6 +1034,62 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } } + // *NOTE: The "identical" variable is currently only used to decide if + // the texgen control should be tentative - this is not used by GLTF + // materials. -Cosmic;2022-11-09 + bool identical = true; // true because it is anded below + bool identical_diffuse = false; + bool identical_norm = false; + bool identical_spec = false; + + LLTextureCtrl *texture_ctrl = getChild<LLTextureCtrl>("texture control"); + LLTextureCtrl *shinytexture_ctrl = getChild<LLTextureCtrl>("shinytexture control"); + LLTextureCtrl *bumpytexture_ctrl = getChild<LLTextureCtrl>("bumpytexture control"); + + LLUUID id; + LLUUID normmap_id; + LLUUID specmap_id; + + LLSelectedTE::getTexId(id, identical_diffuse); + LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm); + LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec); + + static S32 selected_te = -1; + if ((LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()) && + !LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + { + S32 new_selection = -1; // Don't use getLastSelectedTE, it could have been deselected + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + new_selection = te; + break; + } + } + + if (new_selection != selected_te) + { + bool te_has_media = objectp->getTE(new_selection) && objectp->getTE(new_selection)->hasMedia(); + bool te_has_pbr = objectp->getRenderMaterialID(new_selection).notNull(); + + if (te_has_pbr && !((mComboMatMedia->getCurrentIndex() == MATMEDIA_MEDIA) && te_has_media)) + { + mComboMatMedia->selectNthItem(MATMEDIA_PBR); + } + else if (te_has_media) + { + mComboMatMedia->selectNthItem(MATMEDIA_MEDIA); + } + else if (id.notNull() || normmap_id.notNull() || specmap_id.notNull()) + { + mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL); + } + selected_te = new_selection; + } + } + mComboMatMedia->setEnabled(editable); LLRadioGroup* radio_mat_type = getChild<LLRadioGroup>("radio_material_type"); @@ -1043,24 +1111,8 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChildView("checkbox_sync_settings")->setEnabled(editable); childSetValue("checkbox_sync_settings", gSavedSettings.getBOOL("SyncMaterialSettings")); - updateVisibility(); + updateVisibility(objectp); - // *NOTE: The "identical" variable is currently only used to decide if - // the texgen control should be tentative - this is not used by GLTF - // materials. -Cosmic;2022-11-09 - bool identical = true; // true because it is anded below - bool identical_diffuse = false; - bool identical_norm = false; - bool identical_spec = false; - - LLTextureCtrl* texture_ctrl = getChild<LLTextureCtrl>("texture control"); - LLTextureCtrl* shinytexture_ctrl = getChild<LLTextureCtrl>("shinytexture control"); - LLTextureCtrl* bumpytexture_ctrl = getChild<LLTextureCtrl>("bumpytexture control"); - - LLUUID id; - LLUUID normmap_id; - LLUUID specmap_id; - // Color swatch { getChildView("color label")->setEnabled(editable); @@ -1090,9 +1142,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild<LLUICtrl>("ColorTrans")->setValue(editable ? transparency : 0); getChildView("ColorTrans")->setEnabled(editable && has_material); - // Specular map - LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec); - U8 shiny = 0; bool identical_shiny = false; @@ -1158,11 +1207,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) // Texture { - LLSelectedTE::getTexId(id,identical_diffuse); - - // Normal map - LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm); - mIsAlpha = FALSE; LLGLenum image_format = GL_RGB; bool identical_image_format = false; @@ -1801,29 +1845,82 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } } +// One-off listener that updates the build floater UI when the prim inventory updates +class PBRPickerItemListener : public LLVOInventoryListener +{ +protected: + LLViewerObject* mObjectp; + bool mChangePending = true; +public: + + PBRPickerItemListener(LLViewerObject* object) + : mObjectp(object) + { + registerVOInventoryListener(mObjectp, nullptr); + } + + const bool isListeningFor(const LLViewerObject* objectp) const + { + return mChangePending && (objectp == mObjectp); + } + + void inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data) override + { + if (gFloaterTools) + { + gFloaterTools->dirty(); + } + removeVOInventoryListener(); + mChangePending = false; + } + + ~PBRPickerItemListener() + { + removeVOInventoryListener(); + mChangePending = false; + } +}; + void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool& has_faces_without_pbr, bool force_set_values) { has_pbr_material = false; - const bool editable = objectp->permModify() && !objectp->isPermanentEnforced(); bool has_pbr_capabilities = LLMaterialEditor::capabilitiesAvailable(); + bool identical_pbr = true; + const bool settable = has_pbr_capabilities && objectp->permModify() && !objectp->isPermanentEnforced(); + const bool editable = LLMaterialEditor::canModifyObjectsMaterial(); + const bool saveable = LLMaterialEditor::canSaveObjectsMaterial(); // pbr material LLTextureCtrl* pbr_ctrl = findChild<LLTextureCtrl>("pbr_control"); if (pbr_ctrl) { LLUUID pbr_id; - bool identical_pbr; LLSelectedTE::getPbrMaterialId(pbr_id, identical_pbr, has_pbr_material, has_faces_without_pbr); pbr_ctrl->setTentative(identical_pbr ? FALSE : TRUE); - pbr_ctrl->setEnabled(editable && has_pbr_capabilities); + pbr_ctrl->setEnabled(settable); pbr_ctrl->setImageAssetID(pbr_id); } - getChildView("pbr_from_inventory")->setEnabled(editable && has_pbr_capabilities); - getChildView("edit_selected_pbr")->setEnabled(editable && has_pbr_material && !has_faces_without_pbr && has_pbr_capabilities); - getChildView("save_selected_pbr")->setEnabled(objectp->permCopy() && has_pbr_material && !has_faces_without_pbr && has_pbr_capabilities); + getChildView("pbr_from_inventory")->setEnabled(settable); + getChildView("edit_selected_pbr")->setEnabled(editable && !has_faces_without_pbr); + getChildView("save_selected_pbr")->setEnabled(saveable && identical_pbr); + if (objectp->isInventoryPending()) + { + // Reuse the same listener when possible + if (!mInventoryListener || !mInventoryListener->isListeningFor(objectp)) + { + mInventoryListener = std::make_unique<PBRPickerItemListener>(objectp); + } + } + else + { + mInventoryListener = nullptr; + } const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled(); if (show_pbr) @@ -1848,9 +1945,10 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, } } -void LLPanelFace::updateVisibilityGLTF() +void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */) { const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled(); + const bool inventory_pending = objectp && objectp->isInventoryPending(); LLRadioGroup* radio_pbr_type = findChild<LLRadioGroup>("radio_pbr_type"); radio_pbr_type->setVisible(show_pbr); @@ -1861,8 +1959,9 @@ void LLPanelFace::updateVisibilityGLTF() getChildView("pbr_control")->setVisible(show_pbr_render_material_id); getChildView("pbr_from_inventory")->setVisible(show_pbr_render_material_id); - getChildView("edit_selected_pbr")->setVisible(show_pbr_render_material_id); - getChildView("save_selected_pbr")->setVisible(show_pbr_render_material_id); + getChildView("edit_selected_pbr")->setVisible(show_pbr_render_material_id && !inventory_pending); + getChildView("save_selected_pbr")->setVisible(show_pbr_render_material_id && !inventory_pending); + getChildView("material_permissions_loading_label")->setVisible(show_pbr_render_material_id && inventory_pending); getChildView("gltfTextureScaleU")->setVisible(show_pbr); getChildView("gltfTextureScaleV")->setVisible(show_pbr); @@ -2703,7 +2802,7 @@ void LLPanelFace::onCommitMaterialsMedia(LLUICtrl* ctrl, void* userdata) self->refreshMedia(); } -void LLPanelFace::updateVisibility() +void LLPanelFace::updateVisibility(LLViewerObject* objectp /* = nullptr */) { LLRadioGroup* radio_mat_type = findChild<LLRadioGroup>("radio_material_type"); LLRadioGroup* radio_pbr_type = findChild<LLRadioGroup>("radio_pbr_type"); @@ -2794,7 +2893,7 @@ void LLPanelFace::updateVisibility() getChild<LLSpinCtrl>("rptctrl")->setVisible(show_material || show_media); // PBR controls - updateVisibilityGLTF(); + updateVisibilityGLTF(objectp); } // static @@ -3060,11 +3159,7 @@ void LLPanelFace::onSelectPbr(const LLSD& data) { id = pbr_ctrl->getImageAssetID(); } - if (LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id)) - { - LLSelectedTEMaterial::setMaterialID(this, id); - } - else + if (!LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id)) { refresh(); } @@ -4676,13 +4771,6 @@ void LLPanelFace::updateGLTFTextureTransform(float value, U32 pbr_type, std::fun edit(&new_transform); } }); - - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); - if (node) - { - LLViewerObject* object = node->getObject(); - sMaterialOverrideSelection.setObjectUpdatePending(object->getID(), node->getLastSelectedTE()); - } } void LLPanelFace::setMaterialOverridesFromSelection() @@ -4795,17 +4883,22 @@ bool LLPanelFace::Selection::update() return changed; } -void LLPanelFace::Selection::setObjectUpdatePending(const LLUUID &object_id, S32 side) -{ - mPendingObjectID = object_id; - mPendingSide = side; -} - void LLPanelFace::Selection::onSelectedObjectUpdated(const LLUUID& object_id, S32 side) { - if (object_id == mSelectedObjectID && side == mSelectedSide) + if (object_id == mSelectedObjectID) { - mChanged = true; + if (side == mLastSelectedSide) + { + mChanged = true; + } + else if (mLastSelectedSide == -1) // if last selected face was deselected + { + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + if (node && node->isTESelected(side)) + { + mChanged = true; + } + } } } @@ -4818,8 +4911,9 @@ bool LLPanelFace::Selection::compareSelection() mNeedsSelectionCheck = false; const S32 old_object_count = mSelectedObjectCount; + const S32 old_te_count = mSelectedTECount; const LLUUID old_object_id = mSelectedObjectID; - const S32 old_side = mSelectedSide; + const S32 old_side = mLastSelectedSide; LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); LLSelectNode* node = selection->getFirstNode(); @@ -4827,17 +4921,23 @@ bool LLPanelFace::Selection::compareSelection() { LLViewerObject* object = node->getObject(); mSelectedObjectCount = selection->getObjectCount(); + mSelectedTECount = selection->getTECount(); mSelectedObjectID = object->getID(); - mSelectedSide = node->getLastSelectedTE(); + mLastSelectedSide = node->getLastSelectedTE(); } else { mSelectedObjectCount = 0; + mSelectedTECount = 0; mSelectedObjectID = LLUUID::null; - mSelectedSide = -1; + mLastSelectedSide = -1; } - const bool selection_changed = old_object_count != mSelectedObjectCount || old_object_id != mSelectedObjectID || old_side != mSelectedSide; + const bool selection_changed = + old_object_count != mSelectedObjectCount + || old_te_count != mSelectedTECount + || old_object_id != mSelectedObjectID + || old_side != mLastSelectedSide; mChanged = mChanged || selection_changed; return selection_changed; } @@ -4956,23 +5056,24 @@ void LLPanelFace::onPbrSelectionChanged(LLInventoryItem* itemp) LLSaleInfo sale_info; LLSelectMgr::instance().selectGetSaleInfo(sale_info); - bool can_copy = itemp->getPermissions().allowCopyBy(gAgentID); // do we have perm to copy this texture? - bool can_transfer = itemp->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID); // do we have perm to transfer this texture? - bool is_object_owner = gAgentID == obj_owner_id; // does object for which we are going to apply texture belong to the agent? - bool not_for_sale = !sale_info.isForSale(); // is object for which we are going to apply texture not for sale? + bool can_copy = itemp->getPermissions().allowCopyBy(gAgentID); // do we have perm to copy this material? + bool can_transfer = itemp->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID); // do we have perm to transfer this material? + bool can_modify = itemp->getPermissions().allowOperationBy(PERM_MODIFY, gAgentID); // do we have perm to transfer this material? + bool is_object_owner = gAgentID == obj_owner_id; // does object for which we are going to apply material belong to the agent? + bool not_for_sale = !sale_info.isForSale(); // is object for which we are going to apply material not for sale? - if (can_copy && can_transfer) + if (can_copy && can_transfer && can_modify) { pbr_ctrl->setCanApply(true, true); return; } - // if texture has (no-transfer) attribute it can be applied only for object which we own and is not for sale + // if material has (no-transfer) attribute it can be applied only for object which we own and is not for sale pbr_ctrl->setCanApply(false, can_transfer ? true : is_object_owner && not_for_sale); if (gSavedSettings.getBOOL("TextureLivePreview")) { - LLNotificationsUtil::add("LivePreviewUnavailable"); + LLNotificationsUtil::add("LivePreviewUnavailablePBR"); } } } @@ -5058,14 +5159,18 @@ void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical) void LLPanelFace::LLSelectedTE::getPbrMaterialId(LLUUID& id, bool& identical, bool& has_faces_with_pbr, bool& has_faces_without_pbr) { - struct LLSelectedTEGetmatId : public LLSelectedTEGetFunctor<LLUUID> + struct LLSelectedTEGetmatId : public LLSelectedTEFunctor { LLSelectedTEGetmatId() : mHasFacesWithoutPBR(false) , mHasFacesWithPBR(false) + , mIdenticalId(true) + , mIdenticalOverride(true) + , mInitialized(false) + , mMaterialOverride(LLGLTFMaterial::sDefault) { } - LLUUID get(LLViewerObject* object, S32 te_index) + bool apply(LLViewerObject* object, S32 te_index) override { LLUUID pbr_id = object->getRenderMaterialID(te_index); if (pbr_id.isNull()) @@ -5076,12 +5181,49 @@ void LLPanelFace::LLSelectedTE::getPbrMaterialId(LLUUID& id, bool& identical, bo { mHasFacesWithPBR = true; } - return pbr_id; + if (mInitialized) + { + if (mPBRId != pbr_id) + { + mIdenticalId = false; + } + + LLGLTFMaterial* te_override = object->getTE(te_index)->getGLTFMaterialOverride(); + if (te_override) + { + LLGLTFMaterial override = *te_override; + override.sanitizeAssetMaterial(); + mIdenticalOverride &= (override == mMaterialOverride); + } + else + { + mIdenticalOverride &= (mMaterialOverride == LLGLTFMaterial::sDefault); + } + } + else + { + mInitialized = true; + mPBRId = pbr_id; + LLGLTFMaterial* override = object->getTE(te_index)->getGLTFMaterialOverride(); + if (override) + { + mMaterialOverride = *override; + mMaterialOverride.sanitizeAssetMaterial(); + } + } + return true; } bool mHasFacesWithoutPBR; bool mHasFacesWithPBR; + bool mIdenticalId; + bool mIdenticalOverride; + bool mInitialized; + LLGLTFMaterial mMaterialOverride; + LLUUID mPBRId; } func; - identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, id); + LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); + id = func.mPBRId; + identical = func.mIdenticalId && func.mIdenticalOverride; has_faces_with_pbr = func.mHasFacesWithPBR; has_faces_without_pbr = func.mHasFacesWithoutPBR; } diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 7d567c7ce8..d36662c11b 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -35,6 +35,8 @@ #include "lltextureentry.h" #include "llselectmgr.h" +#include <memory> + class LLButton; class LLCheckBoxCtrl; class LLColorSwatchCtrl; @@ -51,6 +53,8 @@ class LLMaterialID; class LLMediaCtrl; class LLMenuButton; +class PBRPickerItemListener; + // Represents an edit for use in replicating the op across one or more materials in the selection set. // // The apply function optionally performs the edit which it implements @@ -105,6 +109,7 @@ public: static void onMaterialOverrideReceived(const LLUUID& object_id, S32 side); + /*virtual*/ void onVisibilityChange(BOOL new_visibility); /*virtual*/ void draw(); LLMaterialPtr createDefaultMaterial(LLMaterialPtr current_material) @@ -292,7 +297,7 @@ private: // // Do NOT call updateUI from within this function. // - void updateVisibility(); + void updateVisibility(LLViewerObject* objectp = nullptr); // Hey look everyone, a type-safe alternative to copy and paste! :) // @@ -453,7 +458,7 @@ private: void onPbrSelectionChanged(LLInventoryItem* itemp); void updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool& has_faces_without_pbr, bool force_set_values); - void updateVisibilityGLTF(); + void updateVisibilityGLTF(LLViewerObject* objectp = nullptr); void updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*)> func); void updateGLTFTextureTransform(float value, U32 pbr_type, std::function<void(LLGLTFMaterial::TextureTransform*)> edit); @@ -482,7 +487,6 @@ private: // Prevents update() returning true until the provided object is // updated. Necessary to prevent controls updating when the mouse is // held down. - void setObjectUpdatePending(const LLUUID &object_id, S32 side); void setDirty() { mChanged = true; }; // Callbacks @@ -497,15 +501,15 @@ private: boost::signals2::scoped_connection mSelectConnection; bool mNeedsSelectionCheck = true; S32 mSelectedObjectCount = 0; + S32 mSelectedTECount = 0; LLUUID mSelectedObjectID; - S32 mSelectedSide = -1; - - LLUUID mPendingObjectID; - S32 mPendingSide = -1; + S32 mLastSelectedSide = -1; }; static Selection sMaterialOverrideSelection; + std::unique_ptr<PBRPickerItemListener> mInventoryListener; + public: #if defined(DEF_GET_MAT_STATE) #undef DEF_GET_MAT_STATE @@ -586,7 +590,6 @@ public: DEF_EDIT_MAT_STATE(LLUUID,const LLUUID&,setNormalID); DEF_EDIT_MAT_STATE(LLUUID,const LLUUID&,setSpecularID); DEF_EDIT_MAT_STATE(LLColor4U, const LLColor4U&,setSpecularLightColor); - DEF_EDIT_MAT_STATE(LLUUID, const LLUUID&, setMaterialID); }; class LLSelectedTE diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index b9b2279e77..0117db86e8 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -241,14 +241,22 @@ void LLPreview::refreshFromItem() // static BOOL LLPreview::canModify(const LLUUID taskUUID, const LLInventoryItem* item) { + const LLViewerObject* object = nullptr; if (taskUUID.notNull()) { - LLViewerObject* object = gObjectList.findObject(taskUUID); - if(object && !object->permModify()) - { - // No permission to edit in-world inventory - return FALSE; - } + object = gObjectList.findObject(taskUUID); + } + + return canModify(object, item); +} + +// static +BOOL LLPreview::canModify(const LLViewerObject* object, const LLInventoryItem* item) +{ + if (object && !object->permModify()) + { + // No permission to edit in-world inventory + return FALSE; } return item && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h index ab60f4c008..3688ee0192 100644 --- a/indra/newview/llpreview.h +++ b/indra/newview/llpreview.h @@ -36,6 +36,7 @@ #include <map> class LLInventoryItem; +class LLViewerObject; class LLLineEditor; class LLRadioGroup; class LLPreview; @@ -109,6 +110,7 @@ public: // We can't modify Item or description in preview if either in-world Object // or Item itself is unmodifiable static BOOL canModify(const LLUUID taskUUID, const LLInventoryItem* item); + static BOOL canModify(const LLViewerObject* object, const LLInventoryItem* item); protected: virtual void onCommit(); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 5c1a339570..6692d124d8 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1773,15 +1773,17 @@ void LLObjectSelection::applyNoCopyTextureToTEs(LLViewerInventoryItem* item) } } -void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) +bool LLObjectSelection::applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* item) { if (!item) { - return; + return false; } LLUUID asset_id = item->getAssetUUID(); + bool material_copied_all_faces = true; + for (iterator iter = begin(); iter != end(); ++iter) { LLSelectNode* node = *iter; @@ -1797,12 +1799,17 @@ void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) { if (node->isTESelected(te)) { - //(no-copy) materials must be moved to the object's inventory only once + //(no-copy), (no-modify), and (no-transfer) materials must be moved to the object's inventory only once // without making any copies if (!material_copied && asset_id.notNull()) { - LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); - material_copied = true; + material_copied = (bool)LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); + } + if (!material_copied) + { + // Applying the material is not possible for this object given the current inventory + material_copied_all_faces = false; + break; } // apply texture for the selected faces @@ -1814,6 +1821,8 @@ void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) } LLGLTFMaterialList::flushUpdates(); + + return material_copied_all_faces; } @@ -1924,6 +1933,8 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) { LLViewerInventoryItem* mItem; LLUUID mMatId; + bool material_copied_any_face = false; + bool material_copied_all_faces = true; f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mMatId(id) {} bool apply(LLViewerObject* objectp, S32 te) { @@ -1950,14 +1961,19 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) } }; - if (item && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) + bool success = true; + if (item && + (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) || + !item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()) || + !item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()) + )) { - getSelection()->applyNoCopyPbrMaterialToTEs(item); + success = success && getSelection()->applyRestrictedPbrMaterialToTEs(item); } else { f setfunc(item, mat_id); - getSelection()->applyToTEs(&setfunc); + success = success && getSelection()->applyToTEs(&setfunc); } struct g : public LLSelectedObjectFunctor @@ -1986,11 +2002,11 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) return true; } } sendfunc(item); - getSelection()->applyToObjects(&sendfunc); + success = success && getSelection()->applyToObjects(&sendfunc); LLGLTFMaterialList::flushUpdates(); - return true; + return success; } //----------------------------------------------------------------------------- diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 327134a487..f89209b437 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -378,7 +378,17 @@ public: * Then this only texture is used for all selected faces. */ void applyNoCopyTextureToTEs(LLViewerInventoryItem* item); - void applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item); + /* + * Multi-purpose function for applying PBR materials to the + * selected object or faces, any combination of copy/mod/transfer + * permission restrictions. This method moves the restricted + * material to the object's inventory and doesn't make a copy of the + * material for each face. Then this only material is used for + * all selected faces. + * Returns false if applying the material failed on one or more selected + * faces. + */ + bool applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* item); ESelectType getSelectType() const { return mSelectType; } @@ -635,7 +645,7 @@ public: void selectionSetRestitution(F32 restitution); void selectionSetMaterial(U8 material); bool selectionSetImage(const LLUUID& imageid); // could be item or asset id - bool selectionSetGLTFMaterial(const LLUUID& mat_id); // could be item or asset id + bool selectionSetGLTFMaterial(const LLUUID& mat_id); // material id only void selectionSetColor(const LLColor4 &color); void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index e84894b395..8ffab761f4 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3617,7 +3617,7 @@ bool process_login_success_response() std::string flag = login_flags["ever_logged_in"]; if(!flag.empty()) { - gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE); + gAgent.setFirstLogin(flag == "N"); } /* Flag is currently ignored by the viewer. @@ -3708,7 +3708,7 @@ bool process_login_success_response() std::string fake_initial_outfit_name = gSavedSettings.getString("FakeInitialOutfitName"); if (!fake_initial_outfit_name.empty()) { - gAgent.setFirstLogin(TRUE); + gAgent.setFirstLogin(true); sInitialOutfit = fake_initial_outfit_name; if (sInitialOutfitGender.empty()) { diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 1918d11964..afcdb26f1a 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -930,6 +930,8 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, LLToolDragAndDrop::ESource source, const LLUUID& src_id) { + if (!item) return FALSE; + // Always succeed if.... // material is from the library // or already in the contents of the object @@ -948,7 +950,14 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->requestInventory(); LLSD args; - args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again."; + if (LLAssetType::AT_MATERIAL == item->getType()) + { + args["ERROR_MESSAGE"] = "Unable to add material.\nPlease wait a few seconds and try again."; + } + else + { + args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again."; + } LLNotificationsUtil::add("ErrorMessage", args); return FALSE; } @@ -957,11 +966,9 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, // if the asset is already in the object's inventory // then it can always be added to a side. // This saves some work if the task's inventory is already loaded - // and ensures that the texture item is only added once. + // and ensures that the asset item is only added once. return TRUE; } - - if (!item) return FALSE; LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) @@ -995,7 +1002,7 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, return FALSE; } } - // Add the texture item to the target object's inventory. + // Add the asset item to the target object's inventory. if (LLAssetType::AT_TEXTURE == new_item->getType() || LLAssetType::AT_MATERIAL == new_item->getType()) { @@ -1005,20 +1012,24 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); } - // TODO: Check to see if adding the item was successful; if not, then - // we should return false here. + // Force the object to update and refetch its inventory so it has this asset. + hit_obj->dirtyInventory(); + hit_obj->requestInventory(); + // TODO: Check to see if adding the item was successful; if not, then + // we should return false here. This will requre a separate listener + // since without listener, we have no way to receive update } else if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) { - // Check that we can add the texture as inventory to the object + // Check that we can add the asset as inventory to the object if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE ) { return FALSE; } // *FIX: may want to make sure agent can paint hit_obj. - // Add the texture item to the target object's inventory. + // Add the asset item to the target object's inventory. if (LLAssetType::AT_TEXTURE == new_item->getType() || LLAssetType::AT_MATERIAL == new_item->getType()) { @@ -1028,7 +1039,27 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); } - // Force the object to update and refetch its inventory so it has this texture. + // Force the object to update and refetch its inventory so it has this asset. + hit_obj->dirtyInventory(); + hit_obj->requestInventory(); + // TODO: Check to see if adding the item was successful; if not, then + // we should return false here. This will requre a separate listener + // since without listener, we have no way to receive update + } + else if (LLAssetType::AT_MATERIAL == new_item->getType() && + !item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) + { + // Check that we can add the material as inventory to the object + if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE ) + { + return FALSE; + } + // *FIX: may want to make sure agent can paint hit_obj. + + // Add the material item to the target object's inventory. + hit_obj->updateMaterialInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); + + // Force the object to update and refetch its inventory so it has this material. hit_obj->dirtyInventory(); hit_obj->requestInventory(); // TODO: Check to see if adding the item was successful; if not, then diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 84cc3f03c2..935d61f7ea 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -183,11 +183,15 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) // an item. BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask) { + BOOL pick_reflection_probe = gSavedSettings.getBOOL("SelectReflectionProbes"); + // don't pick transparent so users can't "pay" transparent objects mPick = gViewerWindow->pickImmediate(x, y, /*BOOL pick_transparent*/ FALSE, /*BOOL pick_rigged*/ TRUE, - /*BOOL pick_particle*/ TRUE); + /*BOOL pick_particle*/ TRUE, + /*BOOL pick_unselectable*/ TRUE, + pick_reflection_probe); mPick.mKeyMask = mask; // claim not handled so UI focus stays same diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index ca87ff0092..0d78bfd753 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -98,6 +98,7 @@ #include "llfloatermyscripts.h" #include "llfloatermyenvironment.h" #include "llfloaternamedesc.h" +#include "llfloaternewfeaturenotification.h" #include "llfloaternotificationsconsole.h" #include "llfloaternotificationstabbed.h" #include "llfloaterobjectweights.h" @@ -229,6 +230,7 @@ public: "avatar_picker", "camera", "camera_presets", + "change_item_thumbnail" "classified", "add_landmark", "delete_pref_preset", @@ -247,6 +249,7 @@ public: "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant "message_tos", // Modal!!! Login specific. "mute_object_by_name", + "new_feature_notification", "publish_classified", "save_pref_preset", "save_camera_preset", @@ -395,6 +398,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>); LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGetBlockedObjectName>); LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMap>); + LLFloaterReg::add("new_feature_notification", "floater_new_feature_notification.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNewFeatureNotification>); LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationConsole>); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 518967709d..aa1827a8e2 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1604,6 +1604,67 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, } } +void move_or_copy_inventory_from_object(const LLUUID& destination_id, + const LLUUID& object_id, + const LLUUID& item_id, + LLPointer<LLInventoryCallback> cb) +{ + LLViewerObject* object = gObjectList.findObject(object_id); + if (!object) + { + return; + } + const LLInventoryItem* item = object->getInventoryItem(item_id); + if (!item) + { + return; + } + + class LLItemAddedObserver : public LLInventoryObserver + { + public: + LLItemAddedObserver(const LLUUID& copied_asset_id, LLPointer<LLInventoryCallback> cb) + : LLInventoryObserver(), + mAssetId(copied_asset_id), + mCallback(cb) + { + } + + void changed(U32 mask) override + { + if((mask & (LLInventoryObserver::ADD)) == 0) + { + return; + } + for (const LLUUID& changed_id : gInventory.getChangedIDs()) + { + LLViewerInventoryItem* changed_item = gInventory.getItem(changed_id); + if (changed_item->getAssetUUID() == mAssetId) + { + changeComplete(changed_item->getUUID()); + return; + } + } + } + + private: + void changeComplete(const LLUUID& item_id) + { + mCallback->fire(item_id); + gInventory.removeObserver(this); + delete this; + } + + LLUUID mAssetId; + LLPointer<LLInventoryCallback> mCallback; + }; + + const LLUUID& asset_id = item->getAssetUUID(); + LLItemAddedObserver* observer = new LLItemAddedObserver(asset_id, cb); + gInventory.addObserver(observer); + object->moveInventory(destination_id, item_id); +} + void create_new_item(const std::string& name, const LLUUID& parent_id, LLAssetType::EType asset_type, diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 24b632632b..6c0f1b8d07 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -439,6 +439,10 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, const LLInventoryItem *src, U32 callback_id = 0); +void move_or_copy_inventory_from_object(const LLUUID& destination_id, + const LLUUID& object_id, + const LLUUID& item_id, + LLPointer<LLInventoryCallback> cb); void menu_create_inventory_item(LLInventoryPanel* root, LLFolderBridge* bridge, diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index d21d6f7027..69e62ace97 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3610,6 +3610,17 @@ LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id) return rv; } +LLInventoryItem* LLViewerObject::getInventoryItem(const LLUUID& item_id) +{ + LLInventoryObject* iobj = getInventoryObject(item_id); + if (!iobj || iobj->getType() == LLAssetType::AT_CATEGORY) + { + return NULL; + } + LLInventoryItem* item = dynamic_cast<LLInventoryItem*>(iobj); + return item; +} + void LLViewerObject::getInventoryContents(LLInventoryObject::object_list_t& objects) { if(mInventory) diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 3665c64965..898b21e1ae 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -494,6 +494,7 @@ public: void updateInventoryLocal(LLInventoryItem* item, U8 key); // Update without messaging. void updateMaterialInventory(LLViewerInventoryItem* item, U8 key, bool is_new); LLInventoryObject* getInventoryObject(const LLUUID& item_id); + LLInventoryItem* getInventoryItem(const LLUUID& item_id); // Get content except for root category void getInventoryContents(LLInventoryObject::object_list_t& objects); diff --git a/indra/newview/skins/default/xui/en/floater_bulk_perms.xml b/indra/newview/skins/default/xui/en/floater_bulk_perms.xml index 431c33a339..7aa31bed71 100644 --- a/indra/newview/skins/default/xui/en/floater_bulk_perms.xml +++ b/indra/newview/skins/default/xui/en/floater_bulk_perms.xml @@ -170,6 +170,20 @@ name="icon_setting" tool_tip="Environment settings" left_pad="2" /> + <check_box + control_name="BulkChangeIncludeMaterials" + height="16" + name="check_materials" + top_pad="5" + left="245" + width="16" /> + <icon + height="16" + image_name="Inv_Material" + mouse_opaque="true" + name="icon_materials" + tool_tip="Materials" + left_pad="2" /> <button height="23" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml new file mode 100644 index 0000000000..c8726d36b4 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + height="130" + width="300" + layout="topleft" + name="floater_new_feature_notification" + title="NEW FEATURE" + show_title="false" + header_height="0" + bg_opaque_image="Window_NoTitle_Foreground" + bg_alpha_image="Window_NoTitle_Background" + can_resize="false" + can_drag_on_left="false" + can_minimize="false" + can_close="false"> + <floater.string name="title_txt_inventory"> +New inventory features + </floater.string> + <floater.string name="description_txt_inventory"> +You can now add preview images to inventory items and view a folder in its own window. +Learn more in this [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ blogpost] + </floater.string> + <floater.string name="title_txt_gltf"> +New GLTF PBR materials support + </floater.string> + <floater.string name="description_txt_gltf"> +You can now use expanded material support with the ability to import and edit GLTF Physically Based Rendering (PBR) Materials. +In order to support the addition of the GLTF format, some areas in the viewer may appear darker than usual. + +Learn more about [https://wiki.secondlife.com/wiki/PBR_Materials Physically Based Rendering (PBR)] + </floater.string> + <text + type="string" + length="1" + follows="top|left|right" + font="SansSerifLargeBold" + text_color="White" + layout="topleft" + left="10" + height="14" + top="10" + right="-10" + name="title_txt"> +New feature + </text> + <text + type="string" + length="1" + follows="top|left|right|bottom" + text_color="White" + layout="topleft" + left="10" + height="40" + top_pad="14" + right="-10" + word_wrap="true" + name="description_txt"> +Feature description + </text> + <button + follows="bottom|left|right" + layout="topleft" + height="24" + label="Got it!" + left="104" + bottom="-10" + name="close_btn" + width="90"/> +</floater> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index ca07aacb21..bbc3798034 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -9211,6 +9211,18 @@ We cannot display a preview of this texture because it is no-copy and/or no-tran <notification icon="alertmodal.tga" + name="LivePreviewUnavailablePBR" + type="alert"> + +We cannot display a preview of this material because it is no-copy, no-transfer, and/or no-modify. + <usetemplate + ignoretext="Warn me that Live Preview mode is not available for no-copy, no-transfer, and/or no-modify materials" + name="okignore" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="FacePasteFailed" type="alertmodal"> Paste failed. [REASON] 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 51e6099ceb..5b15752eb7 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -284,12 +284,26 @@ name="pbr_from_inventory" label="Choose from inventory" width="140"/> + <text + visible="false" + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + top_pad="4" + left_delta="0" + name="material_permissions_loading_label" + text_readonly_color="LabelDisabledColor" + width="160"> + Loading contents... + </text> <button follows="left|top" height="23" layout="topleft" left_delta="0" - top_pad="4" + top_delta="0" name="edit_selected_pbr" label="Edit Selected" width="140"/> |