Merge branch 'release/2025.04' of https://github.com/secondlife/viewer
# Conflicts: # .github/workflows/qatest.yaml # indra/newview/skins/default/xui/en/notifications.xmlmaster
commit
3d61db550d
|
|
@ -0,0 +1 @@
|
|||
qatest.yaml -text eol=crlf
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
name: Run QA Test # Runs automated tests on a self-hosted QA machine
|
||||
permissions:
|
||||
contents: read
|
||||
#pull-requests: write # maybe need to re-add this later
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
|
|
@ -15,11 +18,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Debug Workflow Variables
|
||||
env:
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
HEAD_COMMIT_MSG: ${{ github.event.workflow_run.head_commit.message }}
|
||||
run: |
|
||||
echo "Workflow Conclusion: ${{ github.event.workflow_run.conclusion }}"
|
||||
echo "Workflow Head Branch: ${{ github.event.workflow_run.head_branch }}"
|
||||
echo "Workflow Head Branch: $HEAD_BRANCH"
|
||||
echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
|
||||
echo "Head Commit Message: ${{ github.event.workflow_run.head_commit.message }}"
|
||||
echo "Head Commit Message: $HEAD_COMMIT_MSG"
|
||||
echo "GitHub Ref: ${{ github.ref }}"
|
||||
echo "GitHub Ref Name: ${{ github.ref_name }}"
|
||||
echo "GitHub Event Name: ${{ github.event_name }}"
|
||||
|
|
|
|||
|
|
@ -1076,8 +1076,8 @@ void LLGLSLShader::bind()
|
|||
|
||||
void LLGLSLShader::bind(U8 variant)
|
||||
{
|
||||
llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
|
||||
llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
|
||||
llassert_always(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
|
||||
llassert_always(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
|
||||
mGLTFVariants[variant].bind();
|
||||
}
|
||||
|
||||
|
|
@ -1085,7 +1085,7 @@ void LLGLSLShader::bind(bool rigged)
|
|||
{
|
||||
if (rigged)
|
||||
{
|
||||
llassert(mRiggedVariant);
|
||||
llassert_always(mRiggedVariant);
|
||||
mRiggedVariant->bind();
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -643,6 +643,12 @@ void GLTFSceneManager::render(Asset& asset, U8 variant)
|
|||
return;
|
||||
}
|
||||
|
||||
if (gGLTFPBRMetallicRoughnessProgram.mGLTFVariants.size() <= variant)
|
||||
{
|
||||
llassert(false); // mGLTFVariants should have been initialized
|
||||
return;
|
||||
}
|
||||
|
||||
for (U32 ds = 0; ds < 2; ++ds)
|
||||
{
|
||||
RenderData& rd = asset.mRenderData[ds];
|
||||
|
|
|
|||
|
|
@ -5045,7 +5045,7 @@ U32 LLAppViewer::getTextureCacheVersion()
|
|||
U32 LLAppViewer::getDiskCacheVersion()
|
||||
{
|
||||
// Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes.
|
||||
const U32 DISK_CACHE_VERSION = 2;
|
||||
const U32 DISK_CACHE_VERSION = 3;
|
||||
|
||||
return DISK_CACHE_VERSION ;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3006,6 +3006,7 @@ bool LLInventoryModel::loadSkeleton(
|
|||
bool is_cache_obsolete = false;
|
||||
if (loadFromFile(inventory_filename, categories, items, categories_to_update, is_cache_obsolete))
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED("loadFromFile");
|
||||
// We were able to find a cache of files. So, use what we
|
||||
// found to generate a set of categories we should add. We
|
||||
// will go through each category loaded and if the version
|
||||
|
|
|
|||
|
|
@ -1154,6 +1154,63 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
|
||||
updateVisibility(objectp);
|
||||
|
||||
bool missing_asset = false;
|
||||
{
|
||||
LLGLenum image_format = GL_RGB;
|
||||
bool identical_image_format = false;
|
||||
LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);
|
||||
|
||||
if (!missing_asset)
|
||||
{
|
||||
mIsAlpha = false;
|
||||
switch (image_format)
|
||||
{
|
||||
case GL_RGBA:
|
||||
case GL_ALPHA:
|
||||
{
|
||||
mIsAlpha = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_RGB:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't know image's properties, use material's mode value
|
||||
mIsAlpha = true;
|
||||
}
|
||||
|
||||
// Diffuse Alpha Mode
|
||||
// Init to the default that is appropriate for the alpha content of the asset
|
||||
//
|
||||
U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
|
||||
|
||||
bool identical_alpha_mode = false;
|
||||
|
||||
// See if that's been overridden by a material setting for same...
|
||||
//
|
||||
LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha);
|
||||
|
||||
// it is invalid to have any alpha mode other than blend if transparency is greater than zero ...
|
||||
// Want masking? Want emissive? Tough! You get BLEND!
|
||||
alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode;
|
||||
|
||||
// ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none
|
||||
alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
|
||||
|
||||
mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode);
|
||||
updateAlphaControls();
|
||||
|
||||
mExcludeWater &= (LLMaterial::DIFFUSE_ALPHA_MODE_BLEND == alpha_mode);
|
||||
}
|
||||
|
||||
// Water exclusion
|
||||
{
|
||||
mCheckHideWater->setEnabled(editable && !has_pbr_material && !isMediaTexSelected());
|
||||
|
|
@ -1236,65 +1293,11 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
|
||||
// Texture
|
||||
{
|
||||
LLGLenum image_format = GL_RGB;
|
||||
bool identical_image_format = false;
|
||||
bool missing_asset = false;
|
||||
LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);
|
||||
|
||||
if (!missing_asset)
|
||||
{
|
||||
mIsAlpha = false;
|
||||
switch (image_format)
|
||||
{
|
||||
case GL_RGBA:
|
||||
case GL_ALPHA:
|
||||
{
|
||||
mIsAlpha = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_RGB: break;
|
||||
default:
|
||||
{
|
||||
LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't know image's properties, use material's mode value
|
||||
mIsAlpha = true;
|
||||
}
|
||||
|
||||
if (LLViewerMedia::getInstance()->textureHasMedia(id))
|
||||
{
|
||||
mBtnAlign->setEnabled(editable);
|
||||
}
|
||||
|
||||
// Diffuse Alpha Mode
|
||||
|
||||
// Init to the default that is appropriate for the alpha content of the asset
|
||||
//
|
||||
U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
|
||||
|
||||
bool identical_alpha_mode = false;
|
||||
|
||||
// See if that's been overridden by a material setting for same...
|
||||
//
|
||||
LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha);
|
||||
|
||||
// it is invalid to have any alpha mode other than blend if transparency is greater than zero ...
|
||||
// Want masking? Want emissive? Tough! You get BLEND!
|
||||
alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode;
|
||||
|
||||
// ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none
|
||||
alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
|
||||
|
||||
mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode);
|
||||
|
||||
updateAlphaControls();
|
||||
|
||||
if (mTextureCtrl)
|
||||
{
|
||||
if (identical_diffuse)
|
||||
|
|
@ -4117,6 +4120,85 @@ void LLPanelFace::onPasteColor(LLViewerObject* objectp, S32 te)
|
|||
}
|
||||
}
|
||||
|
||||
void set_item_availability(
|
||||
const LLUUID& id,
|
||||
LLSD& dest,
|
||||
const std::string& modifier,
|
||||
bool is_creator,
|
||||
std::map<LLUUID, LLUUID> &asset_item_map,
|
||||
LLViewerObject* objectp)
|
||||
{
|
||||
if (id.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLUUID item_id;
|
||||
bool from_library = get_is_predefined_texture(id);
|
||||
bool full_perm = from_library;
|
||||
full_perm |= is_creator;
|
||||
|
||||
if (!full_perm)
|
||||
{
|
||||
std::map<LLUUID, LLUUID>::iterator iter = asset_item_map.find(id);
|
||||
if (iter != asset_item_map.end())
|
||||
{
|
||||
item_id = iter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// What this does is simply searches inventory for item with same asset id,
|
||||
// as result it is Hightly unreliable, leaves little control to user, borderline hack
|
||||
// but there are little options to preserve permissions - multiple inventory
|
||||
// items might reference same asset and inventory search is expensive.
|
||||
bool no_transfer = false;
|
||||
if (objectp->getInventoryItemByAsset(id))
|
||||
{
|
||||
no_transfer = !objectp->getInventoryItemByAsset(id)->getIsFullPerm();
|
||||
}
|
||||
item_id = get_copy_free_item_by_asset_id(id, no_transfer);
|
||||
// record value to avoid repeating inventory search when possible
|
||||
asset_item_map[id] = item_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (item_id.notNull() && gInventory.isObjectDescendentOf(item_id, gInventory.getLibraryRootFolderID()))
|
||||
{
|
||||
full_perm = true;
|
||||
from_library = true;
|
||||
}
|
||||
|
||||
dest[modifier + "itemfullperm"] = full_perm;
|
||||
dest[modifier + "fromlibrary"] = from_library;
|
||||
|
||||
// If full permission object, texture is free to copy,
|
||||
// but otherwise we need to check inventory and extract permissions
|
||||
//
|
||||
// Normally we care only about restrictions for current user and objects
|
||||
// don't inherit any 'next owner' permissions from texture, so there is
|
||||
// no need to record item id if full_perm==true
|
||||
if (!full_perm && item_id.notNull())
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (itemp)
|
||||
{
|
||||
LLPermissions item_permissions = itemp->getPermissions();
|
||||
if (item_permissions.allowOperationBy(PERM_COPY,
|
||||
gAgent.getID(),
|
||||
gAgent.getGroupID()))
|
||||
{
|
||||
dest[modifier + "itemid"] = item_id;
|
||||
dest[modifier + "itemfullperm"] = itemp->getIsFullPerm();
|
||||
if (!itemp->isFinished())
|
||||
{
|
||||
// needed for dropTextureAllFaces
|
||||
LLInventoryModelBackgroundFetch::instance().start(item_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPanelFace::onCopyTexture()
|
||||
{
|
||||
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
|
||||
|
|
@ -4154,6 +4236,7 @@ void LLPanelFace::onCopyTexture()
|
|||
if (tep)
|
||||
{
|
||||
LLSD te_data;
|
||||
LLUUID pbr_id = objectp->getRenderMaterialID(te);
|
||||
|
||||
// asLLSD() includes media
|
||||
te_data["te"] = tep->asLLSD();
|
||||
|
|
@ -4162,21 +4245,20 @@ void LLPanelFace::onCopyTexture()
|
|||
te_data["te"]["bumpshiny"] = tep->getBumpShiny();
|
||||
te_data["te"]["bumpfullbright"] = tep->getBumpShinyFullbright();
|
||||
te_data["te"]["texgen"] = tep->getTexGen();
|
||||
te_data["te"]["pbr"] = objectp->getRenderMaterialID(te);
|
||||
te_data["te"]["pbr"] = pbr_id;
|
||||
if (tep->getGLTFMaterialOverride() != nullptr)
|
||||
{
|
||||
te_data["te"]["pbr_override"] = tep->getGLTFMaterialOverride()->asJSON();
|
||||
}
|
||||
|
||||
if (te_data["te"].has("imageid"))
|
||||
if (te_data["te"].has("imageid") || pbr_id.notNull())
|
||||
{
|
||||
LLUUID item_id;
|
||||
LLUUID id = te_data["te"]["imageid"].asUUID();
|
||||
bool from_library = get_is_predefined_texture(id);
|
||||
bool full_perm = from_library;
|
||||
LLUUID img_id = te_data["te"]["imageid"].asUUID();
|
||||
bool pbr_from_library = false;
|
||||
bool pbr_full_perm = false;
|
||||
bool is_creator = false;
|
||||
|
||||
if (!full_perm
|
||||
&& objectp->permCopy()
|
||||
if (objectp->permCopy()
|
||||
&& objectp->permTransfer()
|
||||
&& objectp->permModify())
|
||||
{
|
||||
|
|
@ -4186,66 +4268,31 @@ void LLPanelFace::onCopyTexture()
|
|||
std::string creator_app_link;
|
||||
LLUUID creator_id;
|
||||
LLSelectMgr::getInstance()->selectGetCreator(creator_id, creator_app_link);
|
||||
full_perm = objectp->mOwnerID == creator_id;
|
||||
is_creator = objectp->mOwnerID == creator_id;
|
||||
}
|
||||
|
||||
if (id.notNull() && !full_perm)
|
||||
// check permissions for blin-phong/diffuse image and for pbr asset
|
||||
if (img_id.notNull())
|
||||
{
|
||||
std::map<LLUUID, LLUUID>::iterator iter = asset_item_map.find(id);
|
||||
if (iter != asset_item_map.end())
|
||||
set_item_availability(img_id, te_data["te"], "img", is_creator, asset_item_map, objectp);
|
||||
}
|
||||
if (pbr_id.notNull())
|
||||
{
|
||||
set_item_availability(pbr_id, te_data["te"], "pbr", is_creator, asset_item_map, objectp);
|
||||
|
||||
// permissions for overrides
|
||||
// Overrides do not permit no-copy textures
|
||||
LLGLTFMaterial* override = tep->getGLTFMaterialOverride();
|
||||
if (override != nullptr)
|
||||
{
|
||||
item_id = iter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// What this does is simply searches inventory for item with same asset id,
|
||||
// as result it is Hightly unreliable, leaves little control to user, borderline hack
|
||||
// but there are little options to preserve permissions - multiple inventory
|
||||
// items might reference same asset and inventory search is expensive.
|
||||
bool no_transfer = false;
|
||||
if (objectp->getInventoryItemByAsset(id))
|
||||
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
|
||||
{
|
||||
no_transfer = !objectp->getInventoryItemByAsset(id)->getIsFullPerm();
|
||||
}
|
||||
item_id = get_copy_free_item_by_asset_id(id, no_transfer);
|
||||
// record value to avoid repeating inventory search when possible
|
||||
asset_item_map[id] = item_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (item_id.notNull() && gInventory.isObjectDescendentOf(item_id, gInventory.getLibraryRootFolderID()))
|
||||
{
|
||||
full_perm = true;
|
||||
from_library = true;
|
||||
}
|
||||
|
||||
{
|
||||
te_data["te"]["itemfullperm"] = full_perm;
|
||||
te_data["te"]["fromlibrary"] = from_library;
|
||||
|
||||
// If full permission object, texture is free to copy,
|
||||
// but otherwise we need to check inventory and extract permissions
|
||||
//
|
||||
// Normally we care only about restrictions for current user and objects
|
||||
// don't inherit any 'next owner' permissions from texture, so there is
|
||||
// no need to record item id if full_perm==true
|
||||
if (!full_perm && !from_library && item_id.notNull())
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (itemp)
|
||||
{
|
||||
LLPermissions item_permissions = itemp->getPermissions();
|
||||
if (item_permissions.allowOperationBy(PERM_COPY,
|
||||
gAgent.getID(),
|
||||
gAgent.getGroupID()))
|
||||
LLUUID& texture_id = override->mTextureId[i];
|
||||
if (texture_id.notNull())
|
||||
{
|
||||
te_data["te"]["imageitemid"] = item_id;
|
||||
te_data["te"]["itemfullperm"] = itemp->getIsFullPerm();
|
||||
if (!itemp->isFinished())
|
||||
{
|
||||
// needed for dropTextureAllFaces
|
||||
LLInventoryModelBackgroundFetch::instance().start(item_id, false);
|
||||
}
|
||||
const std::string prefix = "pbr" + std::to_string(i);
|
||||
te_data["te"][prefix + "imageid"] = texture_id;
|
||||
set_item_availability(texture_id, te_data["te"], prefix, is_creator, asset_item_map, objectp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4309,6 +4356,44 @@ void LLPanelFace::onCopyTexture()
|
|||
}
|
||||
}
|
||||
|
||||
bool get_full_permission(const LLSD& te, const std::string &prefix)
|
||||
{
|
||||
return te.has(prefix + "itemfullperm") && te[prefix+"itemfullperm"].asBoolean();
|
||||
}
|
||||
|
||||
bool LLPanelFace::validateInventoryItem(const LLSD& te, const std::string& prefix)
|
||||
{
|
||||
if (te.has(prefix + "itemid"))
|
||||
{
|
||||
LLUUID item_id = te[prefix + "itemid"].asUUID();
|
||||
if (item_id.notNull())
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (!itemp)
|
||||
{
|
||||
// image might be in object's inventory, but it can be not up to date
|
||||
LLSD notif_args;
|
||||
static std::string reason = getString("paste_error_inventory_not_found");
|
||||
notif_args["REASON"] = reason;
|
||||
LLNotificationsUtil::add("FacePasteFailed", notif_args);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item was not found on 'copy' stage
|
||||
// Since this happened at copy, might be better to either show this
|
||||
// at copy stage or to drop clipboard here
|
||||
LLSD notif_args;
|
||||
static std::string reason = getString("paste_error_inventory_not_found");
|
||||
notif_args["REASON"] = reason;
|
||||
LLNotificationsUtil::add("FacePasteFailed", notif_args);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLPanelFace::onPasteTexture()
|
||||
{
|
||||
if (!mClipboardParams.has("texture"))
|
||||
|
|
@ -4373,39 +4458,49 @@ void LLPanelFace::onPasteTexture()
|
|||
for (; iter != end; ++iter)
|
||||
{
|
||||
const LLSD& te_data = *iter;
|
||||
if (te_data.has("te") && te_data["te"].has("imageid"))
|
||||
if (te_data.has("te"))
|
||||
{
|
||||
bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean();
|
||||
full_perm_object &= full_perm;
|
||||
if (!full_perm)
|
||||
if (te_data["te"].has("imageid"))
|
||||
{
|
||||
if (te_data["te"].has("imageitemid"))
|
||||
bool full_perm = get_full_permission(te_data["te"], "img");
|
||||
full_perm_object &= full_perm;
|
||||
if (!full_perm)
|
||||
{
|
||||
LLUUID item_id = te_data["te"]["imageitemid"].asUUID();
|
||||
if (item_id.notNull())
|
||||
if (!validateInventoryItem(te_data["te"], "img"))
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (!itemp)
|
||||
{
|
||||
// image might be in object's inventory, but it can be not up to date
|
||||
LLSD notif_args;
|
||||
static std::string reason = getString("paste_error_inventory_not_found");
|
||||
notif_args["REASON"] = reason;
|
||||
LLNotificationsUtil::add("FacePasteFailed", notif_args);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
if (te_data["te"].has("pbr"))
|
||||
{
|
||||
bool full_perm = get_full_permission(te_data["te"], "pbr");
|
||||
full_perm_object &= full_perm;
|
||||
if (!full_perm)
|
||||
{
|
||||
// Item was not found on 'copy' stage
|
||||
// Since this happened at copy, might be better to either show this
|
||||
// at copy stage or to drop clipboard here
|
||||
LLSD notif_args;
|
||||
static std::string reason = getString("paste_error_inventory_not_found");
|
||||
notif_args["REASON"] = reason;
|
||||
LLNotificationsUtil::add("FacePasteFailed", notif_args);
|
||||
return;
|
||||
if (!validateInventoryItem(te_data["te"], "pbr"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (te_data["te"].has("pbr_override"))
|
||||
{
|
||||
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
|
||||
{
|
||||
const std::string prefix = "pbr" + std::to_string(i);
|
||||
if (te_data["te"].has(prefix + "imageid"))
|
||||
{
|
||||
bool full_perm = get_full_permission(te_data["te"], prefix);
|
||||
full_perm_object &= full_perm;
|
||||
if (!full_perm)
|
||||
{
|
||||
if (!validateInventoryItem(te_data["te"], prefix))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4430,6 +4525,71 @@ void LLPanelFace::onPasteTexture()
|
|||
selected_objects->applyToTEs(&navigate_home_func);
|
||||
}
|
||||
|
||||
void get_item_and_permissions(const LLUUID &id, LLViewerInventoryItem*& itemp, bool& full_perm, bool& from_library, const LLSD &data, const std::string &prefix)
|
||||
{
|
||||
full_perm = get_full_permission(data, prefix);
|
||||
from_library = data.has(prefix + "fromlibrary") && data.get(prefix + "fromlibrary").asBoolean();
|
||||
LLViewerInventoryItem* itemp_res = NULL;
|
||||
|
||||
if (data.has(prefix + "itemid"))
|
||||
{
|
||||
LLUUID item_id = data.get(prefix + "itemid").asUUID();
|
||||
if (item_id.notNull())
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (itemp && itemp->isFinished())
|
||||
{
|
||||
// dropTextureAllFaces will fail if incomplete
|
||||
itemp_res = itemp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Theoretically shouldn't happend, but if it does happen, we
|
||||
// might need to add a notification to user that paste will fail
|
||||
// since inventory isn't fully loaded
|
||||
LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for case when item got removed from inventory after we pressed 'copy'
|
||||
// or texture got pasted into previous object
|
||||
if (!itemp_res && !full_perm)
|
||||
{
|
||||
// Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable.
|
||||
LL_INFOS() << "Item " << data.get(prefix + "itemid").asUUID() << " no longer in inventory." << LL_ENDL;
|
||||
// Todo: fix this, we are often searching same texture multiple times (equal to number of faces)
|
||||
// Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work
|
||||
LLViewerInventoryCategory::cat_array_t cats;
|
||||
LLViewerInventoryItem::item_array_t items;
|
||||
LLAssetIDMatches asset_id_matches(id);
|
||||
gInventory.collectDescendentsIf(LLUUID::null,
|
||||
cats,
|
||||
items,
|
||||
LLInventoryModel::INCLUDE_TRASH,
|
||||
asset_id_matches);
|
||||
|
||||
// Extremely unreliable and perfomance unfriendly.
|
||||
// But we need this to check permissions and it is how texture control finds items
|
||||
for (S32 i = 0; i < items.size(); i++)
|
||||
{
|
||||
LLViewerInventoryItem* itemp = items[i];
|
||||
if (itemp && itemp->isFinished())
|
||||
{
|
||||
// dropTextureAllFaces will fail if incomplete
|
||||
LLPermissions item_permissions = itemp->getPermissions();
|
||||
if (item_permissions.allowOperationBy(PERM_COPY,
|
||||
gAgent.getID(),
|
||||
gAgent.getGroupID()))
|
||||
{
|
||||
itemp_res = itemp;
|
||||
break; // first match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te)
|
||||
{
|
||||
LLSD te_data;
|
||||
|
|
@ -4453,77 +4613,22 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te)
|
|||
if (te_data.has("te"))
|
||||
{
|
||||
// Texture
|
||||
bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean();
|
||||
bool from_library = te_data["te"].has("fromlibrary") && te_data["te"]["fromlibrary"].asBoolean();
|
||||
if (te_data["te"].has("imageid"))
|
||||
{
|
||||
bool img_full_perm = false;
|
||||
bool img_from_library = false;
|
||||
const LLUUID& imageid = te_data["te"]["imageid"].asUUID(); //texture or asset id
|
||||
LLViewerInventoryItem* itemp_res = NULL;
|
||||
LLViewerInventoryItem* img_itemp_res = NULL;
|
||||
|
||||
if (te_data["te"].has("imageitemid"))
|
||||
{
|
||||
LLUUID item_id = te_data["te"]["imageitemid"].asUUID();
|
||||
if (item_id.notNull())
|
||||
{
|
||||
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
|
||||
if (itemp && itemp->isFinished())
|
||||
{
|
||||
// dropTextureAllFaces will fail if incomplete
|
||||
itemp_res = itemp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Theoretically shouldn't happend, but if it does happen, we
|
||||
// might need to add a notification to user that paste will fail
|
||||
// since inventory isn't fully loaded
|
||||
LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for case when item got removed from inventory after we pressed 'copy'
|
||||
// or texture got pasted into previous object
|
||||
if (!itemp_res && !full_perm)
|
||||
{
|
||||
// Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable.
|
||||
LL_INFOS() << "Item " << te_data["te"]["imageitemid"].asUUID() << " no longer in inventory." << LL_ENDL;
|
||||
// Todo: fix this, we are often searching same texture multiple times (equal to number of faces)
|
||||
// Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work
|
||||
LLViewerInventoryCategory::cat_array_t cats;
|
||||
LLViewerInventoryItem::item_array_t items;
|
||||
LLAssetIDMatches asset_id_matches(imageid);
|
||||
gInventory.collectDescendentsIf(LLUUID::null,
|
||||
cats,
|
||||
items,
|
||||
LLInventoryModel::INCLUDE_TRASH,
|
||||
asset_id_matches);
|
||||
get_item_and_permissions(imageid, img_itemp_res, img_full_perm, img_from_library, te_data["te"], "img");
|
||||
|
||||
// Extremely unreliable and perfomance unfriendly.
|
||||
// But we need this to check permissions and it is how texture control finds items
|
||||
for (S32 i = 0; i < items.size(); i++)
|
||||
{
|
||||
LLViewerInventoryItem* itemp = items[i];
|
||||
if (itemp && itemp->isFinished())
|
||||
{
|
||||
// dropTextureAllFaces will fail if incomplete
|
||||
LLPermissions item_permissions = itemp->getPermissions();
|
||||
if (item_permissions.allowOperationBy(PERM_COPY,
|
||||
gAgent.getID(),
|
||||
gAgent.getGroupID()))
|
||||
{
|
||||
itemp_res = itemp;
|
||||
break; // first match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemp_res)
|
||||
if (img_itemp_res)
|
||||
{
|
||||
if (te == -1) // all faces
|
||||
{
|
||||
LLToolDragAndDrop::dropTextureAllFaces(objectp,
|
||||
itemp_res,
|
||||
from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
|
||||
img_itemp_res,
|
||||
img_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
|
||||
LLUUID::null,
|
||||
false);
|
||||
}
|
||||
|
|
@ -4531,15 +4636,15 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te)
|
|||
{
|
||||
LLToolDragAndDrop::dropTextureOneFace(objectp,
|
||||
te,
|
||||
itemp_res,
|
||||
from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
|
||||
img_itemp_res,
|
||||
img_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
|
||||
LLUUID::null,
|
||||
false,
|
||||
0);
|
||||
}
|
||||
}
|
||||
// not an inventory item or no complete items
|
||||
else if (full_perm)
|
||||
else if (img_full_perm)
|
||||
{
|
||||
// Either library, local or existed as fullperm when user made a copy
|
||||
LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(imageid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
|
||||
|
|
@ -4567,17 +4672,65 @@ void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te)
|
|||
// PBR/GLTF
|
||||
if (te_data["te"].has("pbr"))
|
||||
{
|
||||
objectp->setRenderMaterialID(te, te_data["te"]["pbr"].asUUID(), false /*managing our own update*/);
|
||||
tep->setGLTFRenderMaterial(nullptr);
|
||||
tep->setGLTFMaterialOverride(nullptr);
|
||||
const LLUUID pbr_id = te_data["te"]["pbr"].asUUID();
|
||||
bool pbr_full_perm = false;
|
||||
bool pbr_from_library = false;
|
||||
LLViewerInventoryItem* pbr_itemp_res = NULL;
|
||||
|
||||
get_item_and_permissions(pbr_id, pbr_itemp_res, pbr_full_perm, pbr_from_library, te_data["te"], "pbr");
|
||||
|
||||
bool allow = true;
|
||||
|
||||
// check overrides first since they don't need t be moved to inventory
|
||||
if (te_data["te"].has("pbr_override"))
|
||||
{
|
||||
LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID(), te_data["te"]["pbr_override"]);
|
||||
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
|
||||
{
|
||||
const std::string prefix = "pbr" + std::to_string(i);
|
||||
if (te_data["te"].has(prefix + "imageid"))
|
||||
{
|
||||
LLUUID tex_id = te_data["te"][prefix + "imageid"];
|
||||
|
||||
bool full_perm = false;
|
||||
bool from_library = false;
|
||||
LLViewerInventoryItem* itemp_res = NULL;
|
||||
get_item_and_permissions(tex_id, itemp_res, full_perm, from_library, te_data["te"], prefix);
|
||||
allow = full_perm;
|
||||
if (!allow) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (allow && pbr_itemp_res)
|
||||
{
|
||||
LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID());
|
||||
if (pbr_itemp_res)
|
||||
{
|
||||
allow = LLToolDragAndDrop::handleDropMaterialProtections(
|
||||
objectp,
|
||||
pbr_itemp_res,
|
||||
pbr_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
|
||||
pbr_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
allow = pbr_full_perm;
|
||||
}
|
||||
}
|
||||
|
||||
if (allow)
|
||||
{
|
||||
objectp->setRenderMaterialID(te, te_data["te"]["pbr"].asUUID(), false /*managing our own update*/);
|
||||
tep->setGLTFRenderMaterial(nullptr);
|
||||
tep->setGLTFMaterialOverride(nullptr);
|
||||
|
||||
if (te_data["te"].has("pbr_override"))
|
||||
{
|
||||
LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID(), te_data["te"]["pbr_override"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLGLTFMaterialList::queueApply(objectp, te, te_data["te"]["pbr"].asUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -272,6 +272,9 @@ public: // needs to be accessible to selection manager
|
|||
void onCopyTexture();
|
||||
void onPasteTexture();
|
||||
void onPasteTexture(LLViewerObject* objectp, S32 te);
|
||||
private:
|
||||
// for copy/paste operations
|
||||
bool validateInventoryItem(const LLSD& te, const std::string& prefix);
|
||||
|
||||
protected:
|
||||
// <FS> Extended copy & paste buttons
|
||||
|
|
|
|||
|
|
@ -235,7 +235,6 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
|
|||
llassert_always(vertex && normal && tex1);
|
||||
|
||||
U32 surface_stride = mSurfacep->getGridsPerEdge();
|
||||
U32 texture_stride = mSurfacep->getGridsPerEdge() - 1;
|
||||
U32 point_offset = x + y*surface_stride;
|
||||
|
||||
*normal = getNormal(x, y);
|
||||
|
|
@ -248,7 +247,7 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
|
|||
|
||||
// tex0 is used for ownership overlay
|
||||
LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent();
|
||||
LLVector3 tex_pos = rel_pos * (1.f / (texture_stride * mSurfacep->getMetersPerGrid()));
|
||||
LLVector3 tex_pos = rel_pos * (1.f / (surface_stride * mSurfacep->getMetersPerGrid()));
|
||||
tex0->mV[0] = tex_pos.mV[0];
|
||||
tex0->mV[1] = tex_pos.mV[1];
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ bool get_is_predefined_texture(LLUUID asset_id)
|
|||
|| asset_id == DEFAULT_OBJECT_NORMAL
|
||||
|| asset_id == BLANK_OBJECT_NORMAL
|
||||
|| asset_id == IMG_WHITE
|
||||
|| asset_id == LLUUID(SCULPT_DEFAULT_TEXTURE))
|
||||
|| asset_id == LLUUID(SCULPT_DEFAULT_TEXTURE)
|
||||
|| asset_id == BLANK_MATERIAL_ASSET_ID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2463,6 +2463,47 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript(
|
|||
return rv;
|
||||
}
|
||||
|
||||
|
||||
bool is_water_exclusion_face(LLViewerObject* obj, S32 face)
|
||||
{
|
||||
LLViewerTexture* image = obj->getTEImage(face);
|
||||
if (!image)
|
||||
return false;
|
||||
|
||||
// magic texture and alpha blending
|
||||
bool exclude_water = (image->getID() == IMG_ALPHA_GRAD) && obj->isImageAlphaBlended(face);
|
||||
|
||||
// transparency
|
||||
exclude_water &= (obj->getTE(face)->getColor().mV[VALPHA] == 1);
|
||||
|
||||
//absence of normal and specular textures
|
||||
image = obj->getTENormalMap(face);
|
||||
if (image && image != LLViewerFetchedTexture::sDefaultImagep)
|
||||
exclude_water &= image->getID().isNull();
|
||||
image = obj->getTESpecularMap(face);
|
||||
if (image && image != LLViewerFetchedTexture::sDefaultImagep)
|
||||
exclude_water &= image->getID().isNull();
|
||||
|
||||
return exclude_water;
|
||||
}
|
||||
|
||||
bool is_water_exclusion_surface(LLViewerObject* obj, S32 face, bool all_faces)
|
||||
{
|
||||
if (all_faces)
|
||||
{
|
||||
bool exclude_water = false;
|
||||
for (S32 it_face = 0; it_face < obj->getNumTEs(); it_face++)
|
||||
{
|
||||
exclude_water |= is_water_exclusion_face(obj, it_face);
|
||||
}
|
||||
return exclude_water;
|
||||
}
|
||||
else
|
||||
{
|
||||
return is_water_exclusion_face(obj, face);
|
||||
}
|
||||
}
|
||||
|
||||
EAcceptance LLToolDragAndDrop::dad3dApplyToObject(
|
||||
LLViewerObject* obj, S32 face, MASK mask, bool drop, EDragAndDropType cargo_type)
|
||||
{
|
||||
|
|
@ -2553,7 +2594,13 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject(
|
|||
else if (cargo_type == DAD_MATERIAL)
|
||||
{
|
||||
bool all_faces = mask & MASK_SHIFT;
|
||||
if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
|
||||
|
||||
if (is_water_exclusion_surface(obj, face, all_faces))
|
||||
{
|
||||
LLNotificationsUtil::add("WaterExclusionNoMaterial");
|
||||
return ACCEPT_NO;
|
||||
}
|
||||
else if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
|
||||
{
|
||||
dropMaterial(obj, face, item, mSource, mSourceID, all_faces);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14545,6 +14545,15 @@ This will replace the items in the selected outfit with the items you are wearin
|
|||
yestext="Continue"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="notify.tga"
|
||||
name="WaterExclusionNoMaterial"
|
||||
persist="true"
|
||||
type="notify">
|
||||
Unable to apply material to the water exclusion surface.
|
||||
<tag>fail</tag>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="EnableAutoFPSWarning"
|
||||
|
|
|
|||
Loading…
Reference in New Issue