Merge branch 'DRTVWR-559' of github.com:secondlife/viewer into DRTVWR-559
commit
a3a8116060
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@
|
|||
<string>Avatar</string>
|
||||
<string>Voice</string>
|
||||
-->
|
||||
<string>Capabilities</string>
|
||||
</array>
|
||||
</map>
|
||||
</array>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ private:
|
|||
bool mBulkChangeIncludeSounds;
|
||||
bool mBulkChangeIncludeTextures;
|
||||
bool mBulkChangeIncludeSettings;
|
||||
bool mBulkChangeIncludeMaterials;
|
||||
|
||||
bool mBulkChangeShareWithGroup;
|
||||
bool mBulkChangeEveryoneCopy;
|
||||
bool mBulkChangeNextOwnerModify;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
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())
|
||||
if (!LLMaterialEditor::capabilitiesAvailable())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
LLPointer<LLGLTFMaterial> render_material;
|
||||
LLPointer<LLLocalGLTFMaterial> local_material;
|
||||
// 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*/);
|
||||
|
||||
LLViewerObject *objectp = pick.getObject();
|
||||
LLUUID mat_id = objectp->getRenderMaterialID(pick.mObjectFace);
|
||||
if (mat_id.notNull() && objectp->permCopy())
|
||||
LLViewerObject* selected_object = func.mObject;
|
||||
if (!selected_object)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (local_mat)
|
||||
// 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())
|
||||
{
|
||||
local_material = local_mat;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
item_out = selected_object->getInventoryItemByAsset(func.mMaterialId);
|
||||
|
||||
LLPermissions item_permissions;
|
||||
if (item_out)
|
||||
{
|
||||
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;
|
||||
}
|
||||
render_material = tep->getGLTFRenderMaterial();
|
||||
}
|
||||
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;
|
||||
item_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
|
||||
}
|
||||
|
||||
saveMaterialAs(render_material, local_material);
|
||||
// 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)
|
||||
{
|
||||
if (!gAgent.allowOperation(op, object_permissions, GP_OBJECT_MANIPULATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 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
|
||||
{
|
||||
permissions_out.set(object_permissions);
|
||||
}
|
||||
permissions_out.accumulate(floater_perm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLMaterialEditor::saveMaterialAs(const LLGLTFMaterial* render_material, const LLLocalGLTFMaterial *local_material)
|
||||
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);
|
||||
}
|
||||
|
||||
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::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)
|
||||
{
|
||||
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();
|
||||
createInventoryItem(str.str(), new_name, std::string());
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string new_name = response["message"].asString();
|
||||
LLInventoryObject::correctInventoryName(new_name);
|
||||
if (new_name.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -9209,6 +9209,18 @@ We cannot display a preview of this texture because it is no-copy and/or no-tran
|
|||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<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"
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue