summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llprimitive/llmaterial.cpp11
-rw-r--r--indra/llprimitive/llmaterial.h2
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/logcontrol.xml1
-rw-r--r--indra/newview/app_settings/settings.xml13
-rw-r--r--indra/newview/llagent.cpp98
-rw-r--r--indra/newview/llagent.h15
-rw-r--r--indra/newview/llfloaterbulkpermission.cpp5
-rw-r--r--indra/newview/llfloaterbulkpermission.h2
-rw-r--r--indra/newview/llfloaternewfeaturenotification.cpp83
-rw-r--r--indra/newview/llfloaternewfeaturenotification.h49
-rw-r--r--indra/newview/llmaterialeditor.cpp342
-rw-r--r--indra/newview/llmaterialeditor.h11
-rw-r--r--indra/newview/llpanelface.cpp286
-rw-r--r--indra/newview/llpanelface.h19
-rw-r--r--indra/newview/llpreview.cpp20
-rw-r--r--indra/newview/llpreview.h2
-rw-r--r--indra/newview/llselectmgr.cpp36
-rw-r--r--indra/newview/llselectmgr.h14
-rw-r--r--indra/newview/llstartup.cpp4
-rw-r--r--indra/newview/lltooldraganddrop.cpp51
-rw-r--r--indra/newview/lltoolpie.cpp6
-rw-r--r--indra/newview/llviewerfloaterreg.cpp4
-rw-r--r--indra/newview/llviewerinventory.cpp61
-rw-r--r--indra/newview/llviewerinventory.h4
-rw-r--r--indra/newview/llviewerobject.cpp11
-rw-r--r--indra/newview/llviewerobject.h1
-rw-r--r--indra/newview/skins/default/xui/en/floater_bulk_perms.xml14
-rw-r--r--indra/newview/skins/default/xui/en/floater_new_feature_notification.xml69
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml12
-rw-r--r--indra/newview/skins/default/xui/en/panel_tools_texture.xml16
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"/>