phoenix-firestorm/indra/newview/llpanelface.cpp

5781 lines
212 KiB
C++

/**
* @file llpanelface.cpp
* @brief Panel in the tools floater for editing face textures, colors, etc.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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"
// file include
#include "llpanelface.h"
// library includes
#include "llcalc.h"
#include "llerror.h"
#include "llrect.h"
#include "llstring.h"
#include "llfontgl.h"
// project includes
#include "llagent.h"
#include "llagentdata.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcolorswatch.h"
#include "llcombobox.h"
#include "lldrawpoolbump.h"
#include "llface.h"
#include "llgltfmateriallist.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h" // gInventory
#include "llinventorymodelbackgroundfetch.h"
#include "llfloatermediasettings.h"
#include "llfloaterreg.h"
#include "llfloatertools.h"
#include "lllineeditor.h"
#include "llmaterialmgr.h"
#include "llmaterialeditor.h"
#include "llmediactrl.h"
#include "llmediaentry.h"
#include "llmenubutton.h"
#include "llnotificationsutil.h"
#include "llpanelcontents.h"
#include "llradiogroup.h"
#include "llresmgr.h"
#include "llselectmgr.h"
#include "llspinctrl.h"
#include "lltextbox.h"
#include "lltexturectrl.h"
#include "lltextureentry.h"
#include "lltooldraganddrop.h"
#include "lltoolface.h"
#include "lltoolmgr.h"
#include "lltrans.h"
#include "llui.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
#include "llviewerobject.h"
#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
#include "llagent.h"
#include "llfilesystem.h"
#include "llviewerassetupload.h"
#include "llviewermenufile.h"
#include "llsd.h"
#include "llsdutil.h"
#include "llsdserialize.h"
#include "llinventorymodel.h"
#include <unordered_set> // <FS:Zi> Find all faces with same texture
using namespace std::literals;
LLPanelFace::Selection LLPanelFace::sMaterialOverrideSelection;
//
// Constant definitions for comboboxes
// Must match the commbobox definitions in panel_tools_texture.xml
//
const S32 MATMEDIA_MATERIAL = 0; // Material
const S32 MATMEDIA_PBR = 1; // PBR
const S32 MATMEDIA_MEDIA = 2; // Media
const S32 MATTYPE_DIFFUSE = 0; // Diffuse material texture
const S32 MATTYPE_NORMAL = 1; // Normal map
const S32 MATTYPE_SPECULAR = 2; // Specular map
const S32 ALPHAMODE_MASK = 2; // Alpha masking mode
const S32 BUMPY_TEXTURE = 18; // use supplied normal map
const S32 SHINY_TEXTURE = 4; // use supplied specular map
const S32 PBRTYPE_RENDER_MATERIAL_ID = 0; // Render Material ID
const S32 PBRTYPE_BASE_COLOR = 1; // PBR Base Color
const S32 PBRTYPE_METALLIC_ROUGHNESS = 2; // PBR Metallic
const S32 PBRTYPE_EMISSIVE = 3; // PBR Emissive
const S32 PBRTYPE_NORMAL = 4; // PBR Normal
LLGLTFMaterial::TextureInfo LLPanelFace::getPBRTextureInfo()
{
// Radiogroup [ "Complete material", "Base color", "Metallic/roughness", "Emissive", "Normal" ]
S32 radio_group_index = mRadioPbrType->getSelectedIndex();
switch (radio_group_index)
{
case PBRTYPE_BASE_COLOR:
return LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR;
case PBRTYPE_NORMAL:
return LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL;
case PBRTYPE_METALLIC_ROUGHNESS:
return LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS;
case PBRTYPE_EMISSIVE:
return LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE;
}
// The default value is used as a fallback
return LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT;
}
void LLPanelFace::updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*)> func)
{
struct LLSelectedTEGLTFMaterialFunctor : public LLSelectedTEFunctor
{
LLSelectedTEGLTFMaterialFunctor(std::function<void(LLGLTFMaterial*)> func) : mFunc(func) {}
virtual ~LLSelectedTEGLTFMaterialFunctor() {};
bool apply(LLViewerObject* object, S32 face) override
{
LLGLTFMaterial new_override;
const LLTextureEntry* tep = object->getTE(face);
if (tep->getGLTFMaterialOverride())
{
new_override = *tep->getGLTFMaterialOverride();
}
mFunc(&new_override);
LLGLTFMaterialList::queueModify(object, face, &new_override);
return true;
}
std::function<void(LLGLTFMaterial*)> mFunc;
} select_func(func);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);
}
template<typename T>
void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& value, bool& identical, bool has_tolerance, T tolerance)
{
struct LLSelectedTEGetGLTFMaterialFunctor : public LLSelectedTEGetFunctor<T>
{
LLSelectedTEGetGLTFMaterialFunctor(std::function<T(const LLGLTFMaterial*)> func) : mFunc(func) {}
virtual ~LLSelectedTEGetGLTFMaterialFunctor() {};
T get(LLViewerObject* object, S32 face) override
{
const LLTextureEntry* tep = object->getTE(face);
const LLGLTFMaterial* render_material = tep->getGLTFRenderMaterial();
return mFunc(render_material);
}
std::function<T(const LLGLTFMaterial*)> mFunc;
} select_func(func);
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&select_func, value, has_tolerance, tolerance);
}
BOOST_STATIC_ASSERT(MATTYPE_DIFFUSE == LLRender::DIFFUSE_MAP && MATTYPE_NORMAL == LLRender::NORMAL_MAP && MATTYPE_SPECULAR == LLRender::SPECULAR_MAP);
//
// "Use texture" label for normal/specular type comboboxes
// Filled in at initialization from translated strings
//
std::string USE_TEXTURE;
LLRender::eTexIndex LLPanelFace::getTextureChannelToEdit()
{
S32 matmedia_selection = mComboMatMedia->getCurrentIndex();
switch (matmedia_selection)
{
case MATMEDIA_MATERIAL:
return getMatTextureChannel();
case MATMEDIA_PBR:
return getPBRTextureChannel();
}
return (LLRender::eTexIndex)0;
}
LLRender::eTexIndex LLPanelFace::getMatTextureChannel()
{
// Radiogroup [ "Texture (diffuse)", "Bumpiness (normal)", "Shininess (specular)" ]
S32 radio_group_index = mRadioMaterialType->getSelectedIndex();
switch (radio_group_index)
{
case MATTYPE_DIFFUSE: // "Texture (diffuse)"
return LLRender::DIFFUSE_MAP;
case MATTYPE_NORMAL: // "Bumpiness (normal)"
if (getCurrentNormalMap().notNull())
return LLRender::NORMAL_MAP;
break;
case MATTYPE_SPECULAR: // "Shininess (specular)"
if (getCurrentNormalMap().notNull())
return LLRender::SPECULAR_MAP;
break;
}
// The default value is used as a fallback if no required texture is chosen
return (LLRender::eTexIndex)0;
}
LLRender::eTexIndex LLPanelFace::getPBRTextureChannel()
{
// Radiogroup [ "Complete material", "Base color", "Metallic/roughness", "Emissive", "Normal" ]
S32 radio_group_index = mRadioPbrType->getSelectedIndex();
switch (radio_group_index)
{
case PBRTYPE_RENDER_MATERIAL_ID: // "Complete material"
return LLRender::NUM_TEXTURE_CHANNELS;
case PBRTYPE_BASE_COLOR: // "Base color"
return LLRender::BASECOLOR_MAP;
case PBRTYPE_METALLIC_ROUGHNESS: // "Metallic/roughness"
return LLRender::METALLIC_ROUGHNESS_MAP;
case PBRTYPE_EMISSIVE: // "Emissive"
return LLRender::EMISSIVE_MAP;
case PBRTYPE_NORMAL: // "Normal"
return LLRender::GLTF_NORMAL_MAP;
}
// The default value is used as a fallback
return LLRender::NUM_TEXTURE_CHANNELS;
}
LLRender::eTexIndex LLPanelFace::getTextureDropChannel()
{
if (mComboMatMedia->getCurrentIndex() == MATMEDIA_MATERIAL)
{
return getMatTextureChannel();
}
return (LLRender::eTexIndex)0;
}
LLGLTFMaterial::TextureInfo LLPanelFace::getPBRDropChannel()
{
if (mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR)
{
return getPBRTextureInfo();
}
return (LLGLTFMaterial::TextureInfo)0;
}
// Things the UI provides...
//
LLUUID LLPanelFace::getCurrentNormalMap() { return mBumpyTextureCtrl->getImageAssetID(); }
LLUUID LLPanelFace::getCurrentSpecularMap() { return mShinyTextureCtrl->getImageAssetID(); }
U32 LLPanelFace::getCurrentShininess() { return mComboShininess->getCurrentIndex(); }
U32 LLPanelFace::getCurrentBumpiness() { return mComboBumpiness->getCurrentIndex(); }
U8 LLPanelFace::getCurrentDiffuseAlphaMode() { return (U8)mComboAlphaMode->getCurrentIndex(); }
U8 LLPanelFace::getCurrentAlphaMaskCutoff() { return (U8)mMaskCutoff->getValue().asInteger(); }
U8 LLPanelFace::getCurrentEnvIntensity() { return (U8)mEnvironment->getValue().asInteger(); }
U8 LLPanelFace::getCurrentGlossiness() { return (U8)mGlossiness->getValue().asInteger(); }
F32 LLPanelFace::getCurrentBumpyRot() { return (F32)mBumpyRotate->getValue().asReal(); }
F32 LLPanelFace::getCurrentBumpyScaleU() { return (F32)mBumpyScaleU->getValue().asReal(); }
F32 LLPanelFace::getCurrentBumpyScaleV() { return (F32)mBumpyScaleV->getValue().asReal(); }
F32 LLPanelFace::getCurrentBumpyOffsetU() { return (F32)mBumpyOffsetU->getValue().asReal(); }
F32 LLPanelFace::getCurrentBumpyOffsetV() { return (F32)mBumpyOffsetV->getValue().asReal(); }
F32 LLPanelFace::getCurrentShinyRot() { return (F32)mShinyRotate->getValue().asReal(); }
F32 LLPanelFace::getCurrentShinyScaleU() { return (F32)mShinyScaleU->getValue().asReal(); }
F32 LLPanelFace::getCurrentShinyScaleV() { return (F32)mShinyScaleV->getValue().asReal(); }
F32 LLPanelFace::getCurrentShinyOffsetU() { return (F32)mShinyOffsetU->getValue().asReal(); }
F32 LLPanelFace::getCurrentShinyOffsetV() { return (F32)mShinyOffsetV->getValue().asReal(); }
// <FS:CR> UI provided diffuse parameters
F32 LLPanelFace::getCurrentTextureRot() { return (F32)mTexRotate->getValue().asReal(); }
F32 LLPanelFace::getCurrentTextureScaleU() { return (F32)mTexScaleU->getValue().asReal(); }
F32 LLPanelFace::getCurrentTextureScaleV() { return (F32)mTexScaleV->getValue().asReal(); }
F32 LLPanelFace::getCurrentTextureOffsetU() { return (F32)mTexOffsetU->getValue().asReal(); }
F32 LLPanelFace::getCurrentTextureOffsetV() { return (F32)mTexOffsetV->getValue().asReal(); }
// </FS:CR>
//
// Methods
//
bool LLPanelFace::postBuild()
{
getChildSetCommitCallback(mComboShininess, "combobox shininess", [&](LLUICtrl*, const LLSD&) { onCommitShiny(); });
getChildSetCommitCallback(mComboBumpiness, "combobox bumpiness", [&](LLUICtrl*, const LLSD&) { onCommitBump(); });
getChildSetCommitCallback(mComboAlphaMode, "combobox alphamode", [&](LLUICtrl*, const LLSD&) { onCommitAlphaMode(); });
getChildSetCommitCallback(mTexScaleU, "TexScaleU", [&](LLUICtrl*, const LLSD&) { onCommitTextureScaleX(); });
getChildSetCommitCallback(mTexScaleV, "TexScaleV", [&](LLUICtrl*, const LLSD&) { onCommitTextureScaleY(); });
getChildSetCommitCallback(mTexRotate, "TexRot", [&](LLUICtrl*, const LLSD&) { onCommitTextureRot(); });
getChildSetCommitCallback(mTexRepeat, "rptctrl", [&](LLUICtrl*, const LLSD&) { onCommitRepeatsPerMeter(); });
getChildSetCommitCallback(mPlanarAlign, "checkbox planar align", [&](LLUICtrl*, const LLSD&) { onCommitPlanarAlign(); });
getChildSetCommitCallback(mTexOffsetU, "TexOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitTextureOffsetX(); });
getChildSetCommitCallback(mTexOffsetV, "TexOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitTextureOffsetY(); });
getChildSetCommitCallback(mBumpyScaleU, "bumpyScaleU", [&](LLUICtrl*, const LLSD&) { onCommitMaterialBumpyScaleX(); });
getChildSetCommitCallback(mBumpyScaleV, "bumpyScaleV", [&](LLUICtrl*, const LLSD&) { onCommitMaterialBumpyScaleY(); });
getChildSetCommitCallback(mBumpyRotate, "bumpyRot", [&](LLUICtrl*, const LLSD&) { onCommitMaterialBumpyRot(); });
getChildSetCommitCallback(mBumpyOffsetU, "bumpyOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitMaterialBumpyOffsetX(); });
getChildSetCommitCallback(mBumpyOffsetV, "bumpyOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitMaterialBumpyOffsetY(); });
getChildSetCommitCallback(mShinyScaleU, "shinyScaleU", [&](LLUICtrl*, const LLSD&) { onCommitMaterialShinyScaleX(); });
getChildSetCommitCallback(mShinyScaleV, "shinyScaleV", [&](LLUICtrl*, const LLSD&) { onCommitMaterialShinyScaleY(); });
getChildSetCommitCallback(mShinyRotate, "shinyRot", [&](LLUICtrl*, const LLSD&) { onCommitMaterialShinyRot(); });
getChildSetCommitCallback(mShinyOffsetU, "shinyOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitMaterialShinyOffsetX(); });
getChildSetCommitCallback(mShinyOffsetV, "shinyOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitMaterialShinyOffsetY(); });
getChildSetCommitCallback(mGlossiness, "glossiness", [&](LLUICtrl*, const LLSD&) { onCommitMaterialGloss(); });
getChildSetCommitCallback(mEnvironment, "environment", [&](LLUICtrl*, const LLSD&) { onCommitMaterialEnv(); });
getChildSetCommitCallback(mMaskCutoff, "maskcutoff", [&](LLUICtrl*, const LLSD&) { onCommitMaterialMaskCutoff(); });
getChildSetCommitCallback(mAddMedia, "add_media", [&](LLUICtrl*, const LLSD&) { onClickBtnAddMedia(); });
getChildSetCommitCallback(mDelMedia, "delete_media", [&](LLUICtrl*, const LLSD&) { onClickBtnDeleteMedia(); });
getChildSetCommitCallback(mPBRScaleU, "gltfTextureScaleU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleU(); });
getChildSetCommitCallback(mPBRScaleV, "gltfTextureScaleV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleV(); });
getChildSetCommitCallback(mPBRRotate, "gltfTextureRotation", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRotation(); });
getChildSetCommitCallback(mPBROffsetU, "gltfTextureOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetU(); });
getChildSetCommitCallback(mPBROffsetV, "gltfTextureOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetV(); });
LLGLTFMaterialList::addSelectionUpdateCallback(&LLPanelFace::onMaterialOverrideReceived);
sMaterialOverrideSelection.connect();
getChildSetClickedCallback(mBtnAlign, "button align", [&](LLUICtrl*, const LLSD&) { onClickAutoFix(); });
getChildSetClickedCallback(mBtnAlignTex, "button align textures", [&](LLUICtrl*, const LLSD&) { onAlignTexture(); });
//getChildSetClickedCallback(mBtnPbrFromInv, "pbr_from_inventory", [&](LLUICtrl*, const LLSD&) { onClickBtnLoadInvPBR(); }); // <FS> Done via texture picker
getChildSetClickedCallback(mBtnEditBbr, "edit_selected_pbr", [&](LLUICtrl*, const LLSD&) { onClickBtnEditPBR(); });
getChildSetClickedCallback(mBtnSaveBbr, "save_selected_pbr", [&](LLUICtrl*, const LLSD&) { onClickBtnSavePBR(); });
// <FS:CR>
changePrecision(gSavedSettings.getS32("FSBuildToolDecimalPrecision"));
// </FS>
setMouseOpaque(false);
mPBRTextureCtrl = getChild<LLTextureCtrl>("pbr_control");
mPBRTextureCtrl->setDefaultImageAssetID(LLUUID::null);
mPBRTextureCtrl->setBlankImageAssetID(BLANK_MATERIAL_ASSET_ID);
mPBRTextureCtrl->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCommitPbr(); });
mPBRTextureCtrl->setOnCancelCallback([&](LLUICtrl*, const LLSD&) { onCancelPbr(); });
mPBRTextureCtrl->setOnSelectCallback([&](LLUICtrl*, const LLSD&) { onSelectPbr(); });
mPBRTextureCtrl->setDragCallback([&](LLUICtrl*, LLInventoryItem* item) { return onDragPbr(item); });
mPBRTextureCtrl->setOnTextureSelectedCallback([&](LLInventoryItem* item) { onPbrSelectionChanged(item); });
mPBRTextureCtrl->setOnCloseCallback([&](LLUICtrl*, const LLSD& data) { onCloseTexturePicker(data); });
mPBRTextureCtrl->setFollowsTop();
mPBRTextureCtrl->setFollowsLeft();
mPBRTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
mPBRTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
mPBRTextureCtrl->setBakeTextureEnabled(false);
mPBRTextureCtrl->setInventoryPickType(PICK_MATERIAL);
mTextureCtrl = getChild<LLTextureCtrl>("texture control");
mTextureCtrl->setDefaultImageAssetID(DEFAULT_OBJECT_TEXTURE);
mTextureCtrl->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCommitTexture(); });
mTextureCtrl->setOnCancelCallback([&](LLUICtrl*, const LLSD&) { onCancelTexture(); });
mTextureCtrl->setOnSelectCallback([&](LLUICtrl*, const LLSD&) { onSelectTexture(); });
mTextureCtrl->setDragCallback([&](LLUICtrl*, LLInventoryItem* item) { return onDragTexture(item); });
mTextureCtrl->setOnTextureSelectedCallback([&](LLInventoryItem* item) { onTextureSelectionChanged(item); });
mTextureCtrl->setOnCloseCallback([&](LLUICtrl*, const LLSD& data) { onCloseTexturePicker(data); });
mTextureCtrl->setFollowsTop();
mTextureCtrl->setFollowsLeft();
mTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
mTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
mShinyTextureCtrl = getChild<LLTextureCtrl>("shinytexture control");
mShinyTextureCtrl->setDefaultImageAssetID(DEFAULT_OBJECT_SPECULAR);
mShinyTextureCtrl->setCommitCallback([&](LLUICtrl*, const LLSD& data) { onCommitSpecularTexture(data); });
mShinyTextureCtrl->setOnCancelCallback([&](LLUICtrl*, const LLSD& data) { onCancelSpecularTexture(data); });
mShinyTextureCtrl->setOnSelectCallback([&](LLUICtrl*, const LLSD& data) { onSelectSpecularTexture(data); });
mShinyTextureCtrl->setDragCallback([&](LLUICtrl*, LLInventoryItem* item) { return onDragTexture(item); });
mShinyTextureCtrl->setOnTextureSelectedCallback([&](LLInventoryItem* item) { onTextureSelectionChanged(item); });
mShinyTextureCtrl->setOnCloseCallback([&](LLUICtrl*, const LLSD& data) { onCloseTexturePicker(data); });
mShinyTextureCtrl->setFollowsTop();
mShinyTextureCtrl->setFollowsLeft();
mShinyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
mShinyTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
mBumpyTextureCtrl = getChild<LLTextureCtrl>("bumpytexture control");
mBumpyTextureCtrl->setDefaultImageAssetID(DEFAULT_OBJECT_NORMAL);
mBumpyTextureCtrl->setBlankImageAssetID(BLANK_OBJECT_NORMAL);
mBumpyTextureCtrl->setCommitCallback([&](LLUICtrl*, const LLSD& data) { onCommitNormalTexture(data); });
mBumpyTextureCtrl->setOnCancelCallback([&](LLUICtrl*, const LLSD& data) { onCancelNormalTexture(data); });
mBumpyTextureCtrl->setOnSelectCallback([&](LLUICtrl*, const LLSD& data) { onSelectNormalTexture(data); });
mBumpyTextureCtrl->setDragCallback([&](LLUICtrl*, LLInventoryItem* item) { return onDragTexture(item); });
mBumpyTextureCtrl->setOnTextureSelectedCallback([&](LLInventoryItem* item) { onTextureSelectionChanged(item); });
mBumpyTextureCtrl->setOnCloseCallback([&](LLUICtrl*, const LLSD& data) { onCloseTexturePicker(data); });
mBumpyTextureCtrl->setFollowsTop();
mBumpyTextureCtrl->setFollowsLeft();
mBumpyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
mBumpyTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER);
mColorSwatch = getChild<LLColorSwatchCtrl>("colorswatch");
mColorSwatch->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCommitColor(); });
mColorSwatch->setOnCancelCallback([&](LLUICtrl*, const LLSD&) { onCancelColor(); });
mColorSwatch->setOnSelectCallback([&](LLUICtrl*, const LLSD&) { onSelectColor(); });
mColorSwatch->setFollowsTop();
mColorSwatch->setFollowsLeft();
mColorSwatch->setCanApplyImmediately(true);
mShinyColorSwatch = getChild<LLColorSwatchCtrl>("shinycolorswatch");
mShinyColorSwatch->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCommitShinyColor(); });
mShinyColorSwatch->setOnCancelCallback([&](LLUICtrl*, const LLSD&) { onCancelShinyColor(); });
mShinyColorSwatch->setOnSelectCallback([&](LLUICtrl*, const LLSD&) { onSelectShinyColor(); });
mShinyColorSwatch->setFollowsTop();
mShinyColorSwatch->setFollowsLeft();
mShinyColorSwatch->setCanApplyImmediately(true);
mLabelColorTransp = getChild<LLTextBox>("color trans");
mLabelColorTransp->setFollowsTop();
mLabelColorTransp->setFollowsLeft();
mCtrlColorTransp = getChild<LLSpinCtrl>("ColorTrans");
mCtrlColorTransp->setCommitCallback([&](LLUICtrl*, const LLSD&) { onCommitAlpha(); });
mCtrlColorTransp->setPrecision(0);
mCtrlColorTransp->setFollowsTop();
mCtrlColorTransp->setFollowsLeft();
getChildSetCommitCallback(mCheckFullbright, "checkbox fullbright", [&](LLUICtrl*, const LLSD&) { onCommitFullbright(); });
getChildSetCommitCallback(mCheckHideWater, "checkbox_hide_water", [&](LLUICtrl*, const LLSD&) { onCommitHideWater(); });
mLabelTexGen = getChild<LLTextBox>("tex gen");
getChildSetCommitCallback(mComboTexGen, "combobox texgen", [&](LLUICtrl*, const LLSD&) { onCommitTexGen(); });
mComboTexGen->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);
getChildSetCommitCallback(mComboMatMedia, "combobox matmedia", [&](LLUICtrl*, const LLSD&) { onCommitMaterialsMedia(); });
mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
getChildSetCommitCallback(mRadioMaterialType, "radio_material_type", [&](LLUICtrl*, const LLSD&) { onCommitMaterialType(); });
mRadioMaterialType->selectNthItem(MATTYPE_DIFFUSE);
getChildSetCommitCallback(mRadioPbrType, "radio_pbr_type", [&](LLUICtrl*, const LLSD&) { onCommitPbrType(); });
mRadioPbrType->selectNthItem(PBRTYPE_RENDER_MATERIAL_ID);
mLabelGlow = getChild<LLTextBox>("glow label");
getChildSetCommitCallback(mCtrlGlow, "glow", [&](LLUICtrl*, const LLSD&) { onCommitGlow(); });
// <FS> Extended copy & paste buttons
//mMenuClipboardColor = getChild<LLMenuButton>("clipboard_color_params_btn");
//mMenuClipboardTexture = getChild<LLMenuButton>("clipboard_texture_params_btn");
mBtnCopyFaces = getChild<LLButton>("copy_face_btn");
mBtnCopyFaces->setCommitCallback(boost::bind(&LLPanelFace::onCopyFaces, this));
mBtnPasteFaces = getChild<LLButton>("paste_face_btn");
mBtnPasteFaces->setCommitCallback(boost::bind(&LLPanelFace::onPasteFaces, this));
// </FS>
mTitleMedia = getChild<LLMediaCtrl>("title_media");
mTitleMediaText = getChild<LLTextBox>("media_info");
mLabelBumpiness = getChild<LLTextBox>("label bumpiness");
mLabelShininess = getChild<LLTextBox>("label shininess");
mLabelAlphaMode = getChild<LLTextBox>("label alphamode");
mLabelGlossiness = getChild<LLTextBox>("label glossiness");
mLabelEnvironment = getChild<LLTextBox>("label environment");
mLabelMaskCutoff = getChild<LLTextBox>("label maskcutoff");
mLabelShiniColor = getChild<LLTextBox>("label shinycolor");
mLabelColor = getChild<LLTextBox>("color label");
mLabelMatPermLoading = getChild<LLTextBox>("material_permissions_loading_label");
mCheckSyncSettings = getChild<LLCheckBoxCtrl>("checkbox_sync_settings");
// <FS> Build tool enhancements
mCheckSyncSettings->setCommitCallback([&](LLUICtrl*, const LLSD&) { onClickMapsSync(); });
mBtnTexFlipScaleU = getChild<LLButton>("flipTextureScaleU");
mBtnTexFlipScaleV = getChild<LLButton>("flipTextureScaleV");
// </FS>
clearCtrls();
// <FS:Zi> Find all faces with same texture
mBtnSelectSameDiff = getChild<LLButton>("btn_select_same_diff");
mBtnSelectSameSpec = getChild<LLButton>("btn_select_same_norm");
mBtnSelectSameNorm = getChild<LLButton>("btn_select_same_spec");
mBtnSelectSameDiff->setEnabled(false);
mBtnSelectSameSpec->setEnabled(false);
mBtnSelectSameNorm->setEnabled(false);
// </FS:Zi>
return true;
}
LLPanelFace::LLPanelFace()
: LLPanel(),
mIsAlpha(false),
mComboMatMedia(NULL),
mTitleMedia(NULL),
mTitleMediaText(NULL),
mNeedMediaTitle(true)
{
USE_TEXTURE = LLTrans::getString("use_texture");
// <FS> Extended copy & paste buttons
//mCommitCallbackRegistrar.add("PanelFace.menuDoToSelected", boost::bind(&LLPanelFace::menuDoToSelected, this, _2));
//mEnableCallbackRegistrar.add("PanelFace.menuEnable", boost::bind(&LLPanelFace::menuEnableItem, this, _2));
// </FS>
// <FS:Zi> Find all faces with same texture
mCommitCallbackRegistrar.add("BuildTool.Flip", boost::bind(&LLPanelFace::onCommitFlip, this, _2));
mCommitCallbackRegistrar.add("BuildTool.SelectSameTexture", boost::bind(&LLPanelFace::onClickBtnSelectSameTexture, this, _2));
// </FS:Zi>
buildFromFile("panel_tools_texture.xml"); // <FS:Zi> switchable edit texture/materials
}
LLPanelFace::~LLPanelFace()
{
unloadMedia();
}
void LLPanelFace::onVisibilityChange(bool new_visibility)
{
if (new_visibility)
{
gAgent.showLatestFeatureNotification("gltf");
}
LLPanel::onVisibilityChange(new_visibility);
}
void LLPanelFace::draw()
{
updateCopyTexButton();
// grab media name/title and update the UI widget
// Todo: move it, it's preferable not to update
// labels inside draw
updateMediaTitle();
LLPanel::draw();
if (sMaterialOverrideSelection.update())
{
setMaterialOverridesFromSelection();
LLMaterialEditor::updateLive();
}
}
void LLPanelFace::sendTexture()
{
if (!mTextureCtrl->getTentative())
{
// we grab the item id first, because we want to do a
// permissions check in the selection manager. ARGH!
LLUUID id = mTextureCtrl->getImageItemID();
if(id.isNull())
{
id = mTextureCtrl->getImageAssetID();
}
if (!LLSelectMgr::getInstance()->selectionSetImage(id))
{
// need to refresh value in texture ctrl
refresh();
}
}
}
void LLPanelFace::sendBump(U32 bumpiness)
{
if (bumpiness < BUMPY_TEXTURE)
{
LL_DEBUGS("Materials") << "clearing bumptexture control" << LL_ENDL;
mBumpyTextureCtrl->clear();
mBumpyTextureCtrl->setImageAssetID(LLUUID());
}
updateBumpyControls(bumpiness == BUMPY_TEXTURE, true);
LLUUID current_normal_map = mBumpyTextureCtrl->getImageAssetID();
U8 bump = (U8)bumpiness & TEM_BUMP_MASK;
// Clear legacy bump to None when using an actual normal map
if (!current_normal_map.isNull())
{
bump = 0;
}
// Set the normal map or reset it to null as appropriate
//
LLSelectedTEMaterial::setNormalID(this, current_normal_map);
LLSelectMgr::getInstance()->selectionSetBumpmap( bump, mBumpyTextureCtrl->getImageItemID() );
}
void LLPanelFace::sendTexGen()
{
U8 tex_gen = (U8)mComboTexGen->getCurrentIndex() << TEM_TEX_GEN_SHIFT;
LLSelectMgr::getInstance()->selectionSetTexGen( tex_gen );
}
void LLPanelFace::sendShiny(U32 shininess)
{
if (shininess < SHINY_TEXTURE)
{
mShinyTextureCtrl->clear();
mShinyTextureCtrl->setImageAssetID(LLUUID());
}
LLUUID specmap = getCurrentSpecularMap();
U8 shiny = (U8) shininess & TEM_SHINY_MASK;
if (!specmap.isNull())
{
shiny = 0;
}
LLSelectedTEMaterial::setSpecularID(this, specmap);
LLSelectMgr::getInstance()->selectionSetShiny(shiny, mShinyTextureCtrl->getImageItemID());
updateShinyControls(!specmap.isNull(), true);
}
void LLPanelFace::sendFullbright()
{
U8 fullbright = mCheckFullbright->get() ? TEM_FULLBRIGHT_MASK : 0;
LLSelectMgr::getInstance()->selectionSetFullbright(fullbright);
}
void LLPanelFace::sendColor()
{
LLColor4 color = mColorSwatch->get();
LLSelectMgr::getInstance()->selectionSetColorOnly(color);
}
void LLPanelFace::sendAlpha()
{
F32 alpha = (100.f - mCtrlColorTransp->get()) / 100.f;
LLSelectMgr::getInstance()->selectionSetAlphaOnly( alpha );
}
void LLPanelFace::sendGlow()
{
F32 glow = mCtrlGlow->get();
LLSelectMgr::getInstance()->selectionSetGlow(glow);
}
struct LLPanelFaceSetTEFunctor : public LLSelectedTEFunctor
{
LLPanelFaceSetTEFunctor(LLPanelFace* panel) : mPanel(panel) {}
virtual bool apply(LLViewerObject* object, S32 te)
{
LLSpinCtrl *ctrlTexScaleS, *ctrlTexScaleT, *ctrlTexOffsetS, *ctrlTexOffsetT, *ctrlTexRotation;
// Effectively the same as MATMEDIA_PBR sans using different radio,
// separate for the sake of clarity
switch (mPanel->mRadioMaterialType->getSelectedIndex())
{
case MATTYPE_DIFFUSE:
ctrlTexScaleS = mPanel->mTexScaleU;
ctrlTexScaleT = mPanel->mTexScaleV;
ctrlTexOffsetS = mPanel->mTexOffsetU;
ctrlTexOffsetT = mPanel->mTexOffsetV;
ctrlTexRotation = mPanel->mTexRotate;
break;
case MATTYPE_NORMAL:
ctrlTexScaleS = mPanel->mBumpyScaleU;
ctrlTexScaleT = mPanel->mBumpyScaleV;
ctrlTexOffsetS = mPanel->mBumpyOffsetU;
ctrlTexOffsetT = mPanel->mBumpyOffsetV;
ctrlTexRotation = mPanel->mBumpyRotate;
break;
case MATTYPE_SPECULAR:
ctrlTexScaleS = mPanel->mShinyScaleU;
ctrlTexScaleT = mPanel->mShinyScaleV;
ctrlTexOffsetS = mPanel->mShinyOffsetU;
ctrlTexOffsetT = mPanel->mShinyOffsetV;
ctrlTexRotation = mPanel->mShinyRotate;
break;
default:
llassert(false);
return false;
}
bool align_planar = mPanel->mPlanarAlign->get();
llassert(object);
if (ctrlTexScaleS)
{
bool valid = !ctrlTexScaleS->getTentative(); // || !checkFlipScaleS->getTentative();
if (valid || align_planar)
{
F32 value = ctrlTexScaleS->get();
if (mPanel->mComboTexGen->getCurrentIndex() == 1)
{
value *= 0.5f;
}
object->setTEScaleS( te, value );
if (align_planar)
{
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, value, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, value, te, object->getID());
}
}
}
if (ctrlTexScaleT)
{
bool valid = !ctrlTexScaleT->getTentative(); // || !checkFlipScaleT->getTentative();
if (valid || align_planar)
{
F32 value = ctrlTexScaleT->get();
//if (checkFlipScaleT->get())
//{
// value = -value;
//}
if (mPanel->mComboTexGen->getCurrentIndex() == 1)
{
value *= 0.5f;
}
object->setTEScaleT(te, value);
if (align_planar)
{
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, value, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, value, te, object->getID());
}
}
}
if (ctrlTexOffsetS)
{
bool valid = !ctrlTexOffsetS->getTentative();
if (valid || align_planar)
{
F32 value = ctrlTexOffsetS->get();
object->setTEOffsetS(te, value);
if (align_planar)
{
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, value, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, value, te, object->getID());
}
}
}
if (ctrlTexOffsetT)
{
bool valid = !ctrlTexOffsetT->getTentative();
if (valid || align_planar)
{
F32 value = ctrlTexOffsetT->get();
object->setTEOffsetT(te, value);
if (align_planar)
{
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, value, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, value, te, object->getID());
}
}
}
if (ctrlTexRotation)
{
bool valid = !ctrlTexRotation->getTentative();
if (valid || align_planar)
{
F32 value = ctrlTexRotation->get() * DEG_TO_RAD;
object->setTERotation(te, value);
if (align_planar)
{
LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, value, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, value, te, object->getID());
}
}
}
return true;
}
private:
LLPanelFace* mPanel;
};
// Functor that aligns a face to mCenterFace
struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
{
LLPanelFaceSetAlignedTEFunctor(LLPanelFace* panel, LLFace* center_face) :
mPanel(panel),
mCenterFace(center_face) {}
virtual bool apply(LLViewerObject* object, S32 te)
{
LLFace* facep = object->mDrawable->getFace(te);
if (!facep)
{
return true;
}
if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
{
return true;
}
bool set_aligned = true;
if (facep == mCenterFace)
{
set_aligned = false;
}
if (set_aligned)
{
LLVector2 uv_offset, uv_scale;
F32 uv_rot;
set_aligned = facep->calcAlignedPlanarTE(mCenterFace, &uv_offset, &uv_scale, &uv_rot);
if (set_aligned)
{
object->setTEOffset(te, uv_offset.mV[VX], uv_offset.mV[VY]);
object->setTEScale(te, uv_scale.mV[VX], uv_scale.mV[VY]);
object->setTERotation(te, uv_rot);
LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, uv_rot, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, uv_rot, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
}
}
if (!set_aligned)
{
LLPanelFaceSetTEFunctor setfunc(mPanel);
setfunc.apply(object, te);
}
return true;
}
private:
LLPanelFace* mPanel;
LLFace* mCenterFace;
};
struct LLPanelFaceSetAlignedConcreteTEFunctor : public LLSelectedTEFunctor
{
LLPanelFaceSetAlignedConcreteTEFunctor(LLPanelFace* panel, LLFace* center_face, LLRender::eTexIndex map) :
mPanel(panel),
mChefFace(center_face),
mMap(map)
{}
virtual bool apply(LLViewerObject* object, S32 te)
{
LLFace* facep = object->mDrawable->getFace(te);
if (!facep)
{
return true;
}
if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
{
return true;
}
if (mChefFace != facep)
{
LLVector2 uv_offset, uv_scale;
F32 uv_rot;
if (facep->calcAlignedPlanarTE(mChefFace, &uv_offset, &uv_scale, &uv_rot, mMap))
{
switch (mMap)
{
case LLRender::DIFFUSE_MAP:
object->setTEOffset(te, uv_offset.mV[VX], uv_offset.mV[VY]);
object->setTEScale(te, uv_scale.mV[VX], uv_scale.mV[VY]);
object->setTERotation(te, uv_rot);
break;
case LLRender::NORMAL_MAP:
LLPanelFace::LLSelectedTEMaterial::setNormalRotation(mPanel, uv_rot, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setNormalRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
break;
case LLRender::SPECULAR_MAP:
LLPanelFace::LLSelectedTEMaterial::setSpecularRotation(mPanel, uv_rot, te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetX(mPanel, uv_offset.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularOffsetY(mPanel, uv_offset.mV[VY], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
break;
default: /*make compiler happy*/
break;
}
}
}
return true;
}
private:
LLPanelFace* mPanel;
LLFace* mChefFace;
LLRender::eTexIndex mMap;
};
// Functor that tests if a face is aligned to mCenterFace
struct LLPanelFaceGetIsAlignedTEFunctor : public LLSelectedTEFunctor
{
LLPanelFaceGetIsAlignedTEFunctor(LLFace* center_face) :
mCenterFace(center_face) {}
virtual bool apply(LLViewerObject* object, S32 te)
{
LLFace* facep = object->mDrawable->getFace(te);
if (!facep)
{
return false;
}
if (facep->getViewerObject()->getVolume()->getNumVolumeFaces() <= te)
{ //volume face does not exist, can't be aligned
return false;
}
if (facep == mCenterFace)
{
return true;
}
LLVector2 aligned_st_offset, aligned_st_scale;
F32 aligned_st_rot;
if (facep->calcAlignedPlanarTE(mCenterFace, &aligned_st_offset, &aligned_st_scale, &aligned_st_rot))
{
const LLTextureEntry* tep = facep->getTextureEntry();
if (!tep)
{
return false;
}
LLVector2 st_offset, st_scale;
tep->getOffset(&st_offset.mV[VX], &st_offset.mV[VY]);
tep->getScale(&st_scale.mV[VX], &st_scale.mV[VY]);
F32 st_rot = tep->getRotation();
bool eq_offset_x = is_approx_equal_fraction(st_offset.mV[VX], aligned_st_offset.mV[VX], 12);
bool eq_offset_y = is_approx_equal_fraction(st_offset.mV[VY], aligned_st_offset.mV[VY], 12);
bool eq_scale_x = is_approx_equal_fraction(st_scale.mV[VX], aligned_st_scale.mV[VX], 12);
bool eq_scale_y = is_approx_equal_fraction(st_scale.mV[VY], aligned_st_scale.mV[VY], 12);
bool eq_rot = is_approx_equal_fraction(st_rot, aligned_st_rot, 6);
// needs a fuzzy comparison, because of fp errors
if (eq_offset_x &&
eq_offset_y &&
eq_scale_x &&
eq_scale_y &&
eq_rot)
{
return true;
}
}
return false;
}
private:
LLFace* mCenterFace;
};
struct LLPanelFaceSendFunctor : public LLSelectedObjectFunctor
{
virtual bool apply(LLViewerObject* object)
{
object->sendTEUpdate();
return true;
}
};
void LLPanelFace::sendTextureInfo()
{
if (mPlanarAlign->getValue().asBoolean())
{
LLFace* last_face = NULL;
bool identical_face =false;
LLSelectedTE::getFace(last_face, identical_face);
LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
}
else
{
LLPanelFaceSetTEFunctor setfunc(this);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
}
LLPanelFaceSendFunctor sendfunc;
LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
}
void LLPanelFace::alignTextureLayer()
{
LLFace* last_face = NULL;
bool identical_face = false;
LLSelectedTE::getFace(last_face, identical_face);
LLPanelFaceSetAlignedConcreteTEFunctor setfunc(this, last_face, static_cast<LLRender::eTexIndex>(mRadioMaterialType->getSelectedIndex()));
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
}
void LLPanelFace::getState()
{
updateUI();
}
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)
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
LLViewerObject* objectp = node ? node->getObject() : NULL;
if (objectp
&& objectp->getPCode() == LL_PCODE_VOLUME
&& objectp->permModify())
{
bool editable = objectp->permModify() && !objectp->isPermanentEnforced();
bool attachment = objectp->isAttachment();
bool has_pbr_material;
bool has_faces_without_pbr;
updateUIGLTF(objectp, has_pbr_material, has_faces_without_pbr, force_set_values);
const bool has_material = !has_pbr_material;
// only turn on auto-adjust button if there is a media renderer and the media is loaded
mBtnAlign->setEnabled(editable);
// <FS>
bool enable_material_controls = (!gSavedSettings.getBOOL("SyncMaterialSettings"));
if (mComboMatMedia->getCurrentIndex() < MATMEDIA_MATERIAL)
{
// When selecting an object with a pbr and UI combo is not set,
// set to pbr option, otherwise to a texture (material)
if (has_pbr_material)
{
mComboMatMedia->selectNthItem(MATMEDIA_PBR);
}
else
{
mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
}
}
// *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;
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);
LLColor4 color = LLColor4::white;
bool identical_color = false;
LLSelectedTE::getColor(color, identical_color);
F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
mExcludeWater = (id == IMG_ALPHA_GRAD) && normmap_id.isNull() && specmap_id.isNull() && (transparency == 0);
static S32 selected_te = -1;
static LLUUID prev_obj_id;
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)
|| (prev_obj_id != objectp->getID()))
{
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;
prev_obj_id = objectp->getID();
}
}
else
{
if (prev_obj_id != objectp->getID())
{
if (has_pbr_material && (mComboMatMedia->getCurrentIndex() == MATMEDIA_MATERIAL))
{
mComboMatMedia->selectNthItem(MATMEDIA_PBR);
}
else if (!has_pbr_material && (mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR))
{
mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
}
prev_obj_id = objectp->getID();
}
}
mComboMatMedia->setEnabled(editable);
if (mRadioMaterialType->getSelectedIndex() < MATTYPE_DIFFUSE)
{
mRadioMaterialType->selectNthItem(MATTYPE_DIFFUSE);
}
mRadioMaterialType->setEnabled(editable);
if (mRadioPbrType->getSelectedIndex() < PBRTYPE_RENDER_MATERIAL_ID)
{
mRadioPbrType->selectNthItem(PBRTYPE_RENDER_MATERIAL_ID);
}
mRadioPbrType->setEnabled(editable);
const bool pbr_selected = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR;
const bool texture_info_selected = pbr_selected && mRadioPbrType->getSelectedIndex() != PBRTYPE_RENDER_MATERIAL_ID;
mCheckSyncSettings->setEnabled(editable);
mCheckSyncSettings->setValue(gSavedSettings.getBOOL("SyncMaterialSettings"));
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());
mCheckHideWater->set(mExcludeWater);
if (mExcludeWater && !has_pbr_material)
{
mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL);
}
editable &= !mExcludeWater;
// disable controls for water exclusion face after updateVisibility, so the whole panel is not hidden
mComboMatMedia->setEnabled(editable);
mRadioMaterialType->setEnabled(editable);
mRadioPbrType->setEnabled(editable);
mCheckSyncSettings->setEnabled(editable);
}
// Color swatch
mLabelColor->setEnabled(editable);
LLColor4 prev_color = mColorSwatch->get();
mColorSwatch->setOriginal(color);
mColorSwatch->set(color, force_set_values || (prev_color != color) || !editable);
mColorSwatch->setValid(editable && !has_pbr_material);
mColorSwatch->setEnabled( editable && !has_pbr_material);
mColorSwatch->setCanApplyImmediately( editable && !has_pbr_material);
// Color transparency
mLabelColorTransp->setEnabled(editable);
mCtrlColorTransp->setValue(editable ? transparency : 0);
mCtrlColorTransp->setEnabled(editable && has_material);
// Shiny
U8 shiny = 0;
{
bool identical_shiny = false;
LLSelectedTE::getShiny(shiny, identical_shiny);
identical = identical && identical_shiny;
shiny = specmap_id.isNull() ? shiny : SHINY_TEXTURE;
mComboShininess->getSelectionInterface()->selectNthItem((S32)shiny);
mLabelShininess->setEnabled(editable);
mComboShininess->setEnabled(editable);
mLabelGlossiness->setEnabled(editable);
mGlossiness->setEnabled(editable);
mLabelEnvironment->setEnabled(editable);
mEnvironment->setEnabled(editable);
mLabelShiniColor->setEnabled(editable);
mComboShininess->setTentative(!identical_spec);
mGlossiness->setTentative(!identical_spec);
mEnvironment->setTentative(!identical_spec);
mShinyColorSwatch->setTentative(!identical_spec);
mShinyColorSwatch->setValid(editable);
mShinyColorSwatch->setEnabled(editable);
mShinyColorSwatch->setCanApplyImmediately(editable);
}
// Bumpy
U8 bumpy = 0;
{
bool identical_bumpy = false;
LLSelectedTE::getBumpmap(bumpy, identical_bumpy);
LLUUID norm_map_id = getCurrentNormalMap();
bumpy = norm_map_id.isNull() ? bumpy : BUMPY_TEXTURE;
mComboBumpiness->getSelectionInterface()->selectNthItem((S32)bumpy);
mComboBumpiness->setEnabled(editable);
mComboBumpiness->setTentative(!identical_bumpy);
mLabelBumpiness->setEnabled(editable);
}
// Texture
{
if (LLViewerMedia::getInstance()->textureHasMedia(id))
{
mBtnAlign->setEnabled(editable);
}
if (mTextureCtrl)
{
if (identical_diffuse)
{
mTextureCtrl->setTentative(false);
mTextureCtrl->setEnabled(editable && !has_pbr_material);
mTextureCtrl->setImageAssetID(id);
bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material;
mComboAlphaMode->setEnabled(can_change_alpha && transparency <= 0.f);
mLabelAlphaMode->setEnabled(can_change_alpha);
mMaskCutoff->setEnabled(can_change_alpha);
mLabelMaskCutoff->setEnabled(can_change_alpha);
mTextureCtrl->setBakeTextureEnabled(true);
}
else if (id.isNull())
{
// None selected
mTextureCtrl->setTentative(false);
mTextureCtrl->setEnabled(false);
mTextureCtrl->setImageAssetID(LLUUID::null);
mComboAlphaMode->setEnabled(false);
mLabelAlphaMode->setEnabled(false);
mMaskCutoff->setEnabled(false);
mLabelMaskCutoff->setEnabled(false);
mTextureCtrl->setBakeTextureEnabled(false);
}
else
{
// Tentative: multiple selected with different textures
mTextureCtrl->setTentative(true);
mTextureCtrl->setEnabled(editable && !has_pbr_material);
mTextureCtrl->setImageAssetID(id);
bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material;
mComboAlphaMode->setEnabled(can_change_alpha && transparency <= 0.f);
mLabelAlphaMode->setEnabled(can_change_alpha);
mMaskCutoff->setEnabled(can_change_alpha);
mLabelMaskCutoff->setEnabled(can_change_alpha);
mTextureCtrl->setBakeTextureEnabled(true);
}
if (attachment)
{
// attachments are in world and in inventory,
// server doesn't support changing permissions
// in such case
mTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
}
else
{
mTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
}
}
if (mShinyTextureCtrl)
{
mShinyTextureCtrl->setTentative(!identical_spec);
mShinyTextureCtrl->setEnabled(editable && !has_pbr_material);
mShinyTextureCtrl->setImageAssetID(specmap_id);
if (attachment)
{
mShinyTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
}
else
{
mShinyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
}
}
if (mBumpyTextureCtrl)
{
mBumpyTextureCtrl->setTentative(!identical_norm);
mBumpyTextureCtrl->setEnabled(editable && !has_pbr_material);
mBumpyTextureCtrl->setImageAssetID(normmap_id);
if (attachment)
{
mBumpyTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
}
else
{
mBumpyTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
}
}
}
// planar align
bool align_planar = mPlanarAlign->get();
bool identical_planar_aligned = false;
bool enabled = (editable && isIdenticalPlanarTexgen() && !texture_info_selected);
mPlanarAlign->setValue(align_planar && enabled);
mPlanarAlign->setVisible(enabled);
mPlanarAlign->setEnabled(enabled);
mBtnAlignTex->setEnabled(enabled && LLSelectMgr::getInstance()->getSelection()->getObjectCount() > 1);
if (align_planar && enabled)
{
LLFace* last_face = NULL;
bool identical_face = false;
LLSelectedTE::getFace(last_face, identical_face);
LLPanelFaceGetIsAlignedTEFunctor get_is_aligend_func(last_face);
// this will determine if the texture param controls are tentative:
identical_planar_aligned = LLSelectMgr::getInstance()->getSelection()->applyToTEs(&get_is_aligend_func);
}
// Needs to be public and before tex scale settings below to properly reflect
// behavior when in planar vs default texgen modes in the
// NORSPEC-84 et al
//
LLTextureEntry::e_texgen selected_texgen = LLTextureEntry::TEX_GEN_DEFAULT;
bool identical_texgen = true;
bool identical_planar_texgen = false;
LLSelectedTE::getTexGen(selected_texgen, identical_texgen);
identical_planar_texgen = (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));
// Texture scale
{
bool identical_diff_scale_s = false;
bool identical_spec_scale_s = false;
bool identical_norm_scale_s = false;
identical = align_planar ? identical_planar_aligned : identical;
F32 diff_scale_s = 1.f;
F32 spec_scale_s = 1.f;
F32 norm_scale_s = 1.f;
LLSelectedTE::getScaleS(diff_scale_s, identical_diff_scale_s);
LLSelectedTEMaterial::getSpecularRepeatX(spec_scale_s, identical_spec_scale_s);
LLSelectedTEMaterial::getNormalRepeatX(norm_scale_s, identical_norm_scale_s);
diff_scale_s = editable ? diff_scale_s : 1.0f;
diff_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;
norm_scale_s = editable ? norm_scale_s : 1.0f;
norm_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;
spec_scale_s = editable ? spec_scale_s : 1.0f;
spec_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;
mTexScaleU->setValue(diff_scale_s);
mShinyScaleU->setValue(spec_scale_s);
mBumpyScaleU->setValue(norm_scale_s);
mTexScaleU->setEnabled(editable && has_material);
// <FS:CR> Materials alignment
//mShinyScaleU->setEnabled(editable && has_material && specmap_id.notNull());
//mBumpyScaleU->setEnabled(editable && has_material && normmap_id.notNull());
mShinyScaleU->setEnabled(editable && has_material && specmap_id.notNull() && enable_material_controls);
mBumpyScaleU->setEnabled(editable && has_material && normmap_id.notNull() && enable_material_controls);
bool diff_scale_tentative = !(identical && identical_diff_scale_s);
bool norm_scale_tentative = !(identical && identical_norm_scale_s);
bool spec_scale_tentative = !(identical && identical_spec_scale_s);
mTexScaleU->setTentative(LLSD(diff_scale_tentative));
mShinyScaleU->setTentative(LLSD(spec_scale_tentative));
mBumpyScaleU->setTentative(LLSD(norm_scale_tentative));
// <FS:CR> FIRE-11407 - Materials alignment
mCheckSyncSettings->setEnabled(editable && (specmap_id.notNull() || normmap_id.notNull()) && !align_planar);
}
{
bool identical_diff_scale_t = false;
bool identical_spec_scale_t = false;
bool identical_norm_scale_t = false;
F32 diff_scale_t = 1.f;
F32 spec_scale_t = 1.f;
F32 norm_scale_t = 1.f;
LLSelectedTE::getScaleT(diff_scale_t, identical_diff_scale_t);
LLSelectedTEMaterial::getSpecularRepeatY(spec_scale_t, identical_spec_scale_t);
LLSelectedTEMaterial::getNormalRepeatY(norm_scale_t, identical_norm_scale_t);
diff_scale_t = editable ? diff_scale_t : 1.0f;
diff_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;
norm_scale_t = editable ? norm_scale_t : 1.0f;
norm_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;
spec_scale_t = editable ? spec_scale_t : 1.0f;
spec_scale_t *= identical_planar_texgen ? 2.0f : 1.0f;
bool diff_scale_tentative = !identical_diff_scale_t;
bool norm_scale_tentative = !identical_norm_scale_t;
bool spec_scale_tentative = !identical_spec_scale_t;
mTexScaleV->setEnabled(editable && has_material);
// <FS:CR> Materials alignment
//mShinyScaleV->setEnabled(editable && has_material && specmap_id.notNull());
//mBumpyScaleV->setEnabled(editable && has_material && normmap_id.notNull());
mShinyScaleV->setEnabled(editable && has_material && specmap_id.notNull() && enable_material_controls);
mBumpyScaleV->setEnabled(editable && has_material && normmap_id.notNull() && enable_material_controls);
if (force_set_values)
{
mTexScaleV->forceSetValue(diff_scale_t);
}
else
{
mTexScaleV->setValue(diff_scale_t);
}
mShinyScaleV->setValue(spec_scale_t);
mBumpyScaleV->setValue(norm_scale_t);
mTexScaleV->setTentative(LLSD(diff_scale_tentative));
mShinyScaleV->setTentative(LLSD(spec_scale_tentative));
mBumpyScaleV->setTentative(LLSD(norm_scale_tentative));
}
// Texture offset
{
bool identical_diff_offset_s = false;
bool identical_norm_offset_s = false;
bool identical_spec_offset_s = false;
F32 diff_offset_s = 0.0f;
F32 norm_offset_s = 0.0f;
F32 spec_offset_s = 0.0f;
LLSelectedTE::getOffsetS(diff_offset_s, identical_diff_offset_s);
LLSelectedTEMaterial::getNormalOffsetX(norm_offset_s, identical_norm_offset_s);
LLSelectedTEMaterial::getSpecularOffsetX(spec_offset_s, identical_spec_offset_s);
bool diff_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_diff_offset_s);
bool norm_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_norm_offset_s);
bool spec_offset_u_tentative = !(align_planar ? identical_planar_aligned : identical_spec_offset_s);
mTexOffsetU->setValue(editable ? diff_offset_s : 0.0f);
mBumpyOffsetU->setValue(editable ? norm_offset_s : 0.0f);
mShinyOffsetU->setValue(editable ? spec_offset_s : 0.0f);
mTexOffsetU->setTentative(LLSD(diff_offset_u_tentative));
mShinyOffsetU->setTentative(LLSD(spec_offset_u_tentative));
mBumpyOffsetU->setTentative(LLSD(norm_offset_u_tentative));
mTexOffsetU->setEnabled(editable && has_material);
// <FS:CR> Materials alignment
//mShinyOffsetU->setEnabled(editable && has_material && specmap_id.notNull());
//mBumpyOffsetU->setEnabled(editable && has_material && normmap_id.notNull());
mShinyOffsetU->setEnabled(editable && has_material && specmap_id.notNull() && enable_material_controls);
mBumpyOffsetU->setEnabled(editable && has_material && normmap_id.notNull() && enable_material_controls);
}
{
bool identical_diff_offset_t = false;
bool identical_norm_offset_t = false;
bool identical_spec_offset_t = false;
F32 diff_offset_t = 0.0f;
F32 norm_offset_t = 0.0f;
F32 spec_offset_t = 0.0f;
LLSelectedTE::getOffsetT(diff_offset_t, identical_diff_offset_t);
LLSelectedTEMaterial::getNormalOffsetY(norm_offset_t, identical_norm_offset_t);
LLSelectedTEMaterial::getSpecularOffsetY(spec_offset_t, identical_spec_offset_t);
bool diff_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_diff_offset_t);
bool norm_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_norm_offset_t);
bool spec_offset_v_tentative = !(align_planar ? identical_planar_aligned : identical_spec_offset_t);
mTexOffsetV->setValue( editable ? diff_offset_t : 0.0f);
mBumpyOffsetV->setValue(editable ? norm_offset_t : 0.0f);
mShinyOffsetV->setValue(editable ? spec_offset_t : 0.0f);
mTexOffsetV->setTentative(LLSD(diff_offset_v_tentative));
mBumpyOffsetV->setTentative(LLSD(norm_offset_v_tentative));
mShinyOffsetV->setTentative(LLSD(spec_offset_v_tentative));
mTexOffsetV->setEnabled(editable && has_material);
// <FS:CR> Materials alignment
//mShinyOffsetV->setEnabled(editable && has_material && specmap_id.notNull());
//mBumpyOffsetV->setEnabled(editable && has_material && normmap_id.notNull());
mShinyOffsetV->setEnabled(editable && has_material && specmap_id.notNull() && enable_material_controls);
mBumpyOffsetV->setEnabled(editable && has_material && normmap_id.notNull() && enable_material_controls);
}
// Texture rotation
{
bool identical_diff_rotation = false;
bool identical_norm_rotation = false;
bool identical_spec_rotation = false;
F32 diff_rotation = 0.f;
F32 norm_rotation = 0.f;
F32 spec_rotation = 0.f;
LLSelectedTE::getRotation(diff_rotation, identical_diff_rotation);
LLSelectedTEMaterial::getSpecularRotation(spec_rotation, identical_spec_rotation);
LLSelectedTEMaterial::getNormalRotation(norm_rotation, identical_norm_rotation);
bool diff_rot_tentative = !(align_planar ? identical_planar_aligned : identical_diff_rotation);
bool norm_rot_tentative = !(align_planar ? identical_planar_aligned : identical_norm_rotation);
bool spec_rot_tentative = !(align_planar ? identical_planar_aligned : identical_spec_rotation);
F32 diff_rot_deg = diff_rotation * RAD_TO_DEG;
F32 norm_rot_deg = norm_rotation * RAD_TO_DEG;
F32 spec_rot_deg = spec_rotation * RAD_TO_DEG;
mTexRotate->setEnabled(editable && has_material);
// <FS:CR> Materials alignment
//mShinyRotate->setEnabled(editable && has_material && specmap_id.notNull());
//mBumpyRotate->setEnabled(editable && has_material && normmap_id.notNull());
mShinyRotate->setEnabled(editable && has_material && specmap_id.notNull() && enable_material_controls);
mBumpyRotate->setEnabled(editable && has_material && normmap_id.notNull() && enable_material_controls);
mTexRotate->setTentative(LLSD(diff_rot_tentative));
mShinyRotate->setTentative(LLSD(spec_rot_tentative));
mBumpyRotate->setTentative(LLSD(norm_rot_tentative));
mTexRotate->setValue(editable ? diff_rot_deg : 0.0f);
mShinyRotate->setValue(editable ? spec_rot_deg : 0.0f);
mBumpyRotate->setValue(editable ? norm_rot_deg : 0.0f);
}
{
F32 glow = 0.f;
bool identical_glow = false;
LLSelectedTE::getGlow(glow, identical_glow);
mCtrlGlow->setValue(glow);
mCtrlGlow->setTentative(!identical_glow);
mCtrlGlow->setEnabled(editable);
mLabelGlow->setEnabled(editable);
}
{
// Maps from enum to combobox entry index
mComboTexGen->selectNthItem(((S32)selected_texgen) >> 1);
mComboTexGen->setEnabled(editable);
mComboTexGen->setTentative(!identical);
mLabelTexGen->setEnabled(editable);
}
{
U8 fullbright_flag = 0;
bool identical_fullbright = false;
LLSelectedTE::getFullbright(fullbright_flag, identical_fullbright);
mCheckFullbright->setValue((S32)(fullbright_flag != 0));
mCheckFullbright->setEnabled(editable && !has_pbr_material);
mCheckFullbright->setTentative(!identical_fullbright);
mComboMatMedia->setEnabledByValue("Materials", !has_pbr_material);
}
// Repeats per meter
{
F32 repeats_diff = 1.f;
F32 repeats_norm = 1.f;
F32 repeats_spec = 1.f;
bool identical_diff_repeats = false;
bool identical_norm_repeats = false;
bool identical_spec_repeats = false;
LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);
{
S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0;
bool enabled = editable && (index != 1);
bool identical_repeats = true;
S32 material_selection = mComboMatMedia->getCurrentIndex();
F32 repeats = 1.0f;
U32 material_type = MATTYPE_DIFFUSE;
if (material_selection == MATMEDIA_MATERIAL)
{
material_type = mRadioMaterialType->getSelectedIndex();
}
else if (material_selection == MATMEDIA_PBR)
{
enabled = editable && has_pbr_material;
material_type = mRadioPbrType->getSelectedIndex();
}
switch (material_type)
{
default:
case MATTYPE_DIFFUSE:
if (material_selection != MATMEDIA_PBR)
{
enabled = editable && !id.isNull();
}
identical_repeats = identical_diff_repeats;
repeats = repeats_diff;
break;
case MATTYPE_SPECULAR:
if (material_selection != MATMEDIA_PBR)
{
enabled = (editable && ((shiny == SHINY_TEXTURE) && !specmap_id.isNull())
&& enable_material_controls); // <FS:CR> Materials Alignment
}
identical_repeats = identical_spec_repeats;
repeats = repeats_spec;
break;
case MATTYPE_NORMAL:
if (material_selection != MATMEDIA_PBR)
{
enabled = (editable && ((bumpy == BUMPY_TEXTURE) && !normmap_id.isNull())
&& enable_material_controls); // <FS:CR> Materials Alignment
}
identical_repeats = identical_norm_repeats;
repeats = repeats_norm;
break;
}
bool repeats_tentative = !identical_repeats;
if (force_set_values)
{
// onCommit, previosly edited element updates related ones
mTexRepeat->forceSetValue(editable ? repeats : 1.0f);
}
else
{
mTexRepeat->setValue(editable ? repeats : 1.0f);
}
mTexRepeat->setTentative(LLSD(repeats_tentative));
mTexRepeat->setEnabled(has_material && !identical_planar_texgen && enabled);
// <FS:CR> FIRE-11407 - Flip buttons
mBtnTexFlipScaleU->setEnabled(enabled);
mBtnTexFlipScaleV->setEnabled(enabled);
// </FS:CR>
}
}
// Materials
{
LLMaterialPtr material;
LLSelectedTEMaterial::getCurrent(material, identical);
if (material && editable)
{
LL_DEBUGS("Materials") << material->asLLSD() << LL_ENDL;
// Alpha
{
U32 alpha_mode = material->getDiffuseAlphaMode();
if (transparency > 0.f)
{ //it is invalid to have any alpha mode other than blend if transparency is greater than zero ...
alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_BLEND;
}
if (!mIsAlpha)
{ // ... unless there is no alpha channel in the texture, in which case alpha mode MUST ebe none
alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
}
mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode);
}
mMaskCutoff->setValue(material->getAlphaMaskCutoff());
updateAlphaControls();
identical_planar_texgen = isIdenticalPlanarTexgen();
// Shiny (specular)
F32 offset_x, offset_y, repeat_x, repeat_y, rot;
mShinyTextureCtrl->setImageAssetID(material->getSpecularID());
if (!material->getSpecularID().isNull() && (shiny == SHINY_TEXTURE))
{
material->getSpecularOffset(offset_x, offset_y);
material->getSpecularRepeat(repeat_x, repeat_y);
if (identical_planar_texgen)
{
repeat_x *= 2.0f;
repeat_y *= 2.0f;
}
rot = material->getSpecularRotation();
mShinyScaleU->setValue(repeat_x);
mShinyScaleV->setValue(repeat_y);
mShinyRotate->setValue(rot * RAD_TO_DEG);
mShinyOffsetU->setValue(offset_x);
mShinyOffsetV->setValue(offset_y);
mGlossiness->setValue(material->getSpecularLightExponent());
mEnvironment->setValue(material->getEnvironmentIntensity());
updateShinyControls(!material->getSpecularID().isNull(), true);
}
// Assert desired colorswatch color to match material AFTER updateShinyControls
// to avoid getting overwritten with the default on some UI state changes.
//
if (!material->getSpecularID().isNull())
{
LLColor4 new_color = material->getSpecularLightColor();
LLColor4 old_color = mShinyColorSwatch->get();
mShinyColorSwatch->setOriginal(new_color);
mShinyColorSwatch->set(new_color, force_set_values || old_color != new_color || !editable);
}
// Bumpy (normal)
mBumpyTextureCtrl->setImageAssetID(material->getNormalID());
if (!material->getNormalID().isNull())
{
material->getNormalOffset(offset_x,offset_y);
material->getNormalRepeat(repeat_x,repeat_y);
if (identical_planar_texgen)
{
repeat_x *= 2.0f;
repeat_y *= 2.0f;
}
rot = material->getNormalRotation();
mBumpyScaleU->setValue(repeat_x);
mBumpyScaleV->setValue(repeat_y);
mBumpyRotate->setValue(rot*RAD_TO_DEG);
mBumpyOffsetU->setValue(offset_x);
mBumpyOffsetV->setValue(offset_y);
updateBumpyControls(!material->getNormalID().isNull(), true);
}
}
}
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
bool single_volume = (selected_count == 1);
// <FS> Extended copy & paste buttons
//mMenuClipboardColor->setEnabled(editable && single_volume);
mBtnCopyFaces->setEnabled(editable && single_volume);
mBtnPasteFaces->setEnabled(editable && !mClipboardParams.emptyMap() && (mClipboardParams.has("color") || mClipboardParams.has("texture")));
// </FS>
// Set variable values for numeric expressions
LLCalc* calcp = LLCalc::getInstance();
calcp->setVar(LLCalc::TEX_U_SCALE, (F32)mTexScaleU->getValue().asReal());
calcp->setVar(LLCalc::TEX_V_SCALE, (F32)mTexScaleV->getValue().asReal());
calcp->setVar(LLCalc::TEX_U_OFFSET, (F32)mTexOffsetU->getValue().asReal());
calcp->setVar(LLCalc::TEX_V_OFFSET, (F32)mTexOffsetV->getValue().asReal());
calcp->setVar(LLCalc::TEX_ROTATION, (F32)mTexRotate->getValue().asReal());
calcp->setVar(LLCalc::TEX_TRANSPARENCY, (F32)mCtrlColorTransp->getValue().asReal());
calcp->setVar(LLCalc::TEX_GLOW, (F32)mCtrlGlow->getValue().asReal());
// <FS:Zi> Find all faces with same texture
mBtnSelectSameDiff->setEnabled(LLSelectMgr::getInstance()->getTEMode() && mTextureCtrl->getEnabled());
mBtnSelectSameNorm->setEnabled(LLSelectMgr::getInstance()->getTEMode() && mBumpyTextureCtrl->getEnabled());
mBtnSelectSameSpec->setEnabled(LLSelectMgr::getInstance()->getTEMode() && mShinyTextureCtrl->getEnabled());
// </FS:Zi>
}
else
{
// Disable all UICtrls
clearCtrls();
// Disable non-UICtrls
if (mPBRTextureCtrl)
{
mPBRTextureCtrl->setImageAssetID(LLUUID::null);
mPBRTextureCtrl->setEnabled(false);
}
if (mTextureCtrl)
{
mTextureCtrl->setImageAssetID( LLUUID::null );
mTextureCtrl->setEnabled( false ); // this is a LLUICtrl, but we don't want it to have keyboard focus so we add it as a child, not a ctrl.
// mTextureCtrl->setValid(false);
}
if (mColorSwatch)
{
mColorSwatch->setEnabled( false );
mColorSwatch->setFallbackImage(LLUI::getUIImage("locked_image.j2c") );
mColorSwatch->setValid(false);
}
if (mRadioMaterialType)
{
mRadioMaterialType->setSelectedIndex(0);
}
mLabelColorTransp->setEnabled(false);
mTexRepeat->setEnabled(false);
mLabelTexGen->setEnabled(false);
mLabelShininess->setEnabled(false);
mLabelBumpiness->setEnabled(false);
mBtnAlign->setEnabled(false);
//mBtnPbrFromInv->setEnabled(false); // <FS> Done via texture picker
mBtnEditBbr->setEnabled(false);
mBtnSaveBbr->setEnabled(false);
// <FS> Extended copy & paste buttons
mBtnCopyFaces->setEnabled(false);
mBtnPasteFaces->setEnabled(false);
// </FS>
updateVisibility();
// Set variable values for numeric expressions
LLCalc* calcp = LLCalc::getInstance();
calcp->clearVar(LLCalc::TEX_U_SCALE);
calcp->clearVar(LLCalc::TEX_V_SCALE);
calcp->clearVar(LLCalc::TEX_U_OFFSET);
calcp->clearVar(LLCalc::TEX_V_OFFSET);
calcp->clearVar(LLCalc::TEX_ROTATION);
calcp->clearVar(LLCalc::TEX_TRANSPARENCY);
calcp->clearVar(LLCalc::TEX_GLOW);
}
}
// One-off listener that updates the build floater UI when the agent inventory adds or removes an item
class PBRPickerAgentListener : public LLInventoryObserver
{
protected:
bool mChangePending = true;
public:
PBRPickerAgentListener() : LLInventoryObserver()
{
gInventory.addObserver(this);
}
const bool isListening()
{
return mChangePending;
}
void changed(U32 mask) override
{
if (!(mask & (ADD | REMOVE)))
{
return;
}
if (gFloaterTools)
{
gFloaterTools->dirty();
}
gInventory.removeObserver(this);
mChangePending = false;
}
~PBRPickerAgentListener() override
{
gInventory.removeObserver(this);
mChangePending = false;
}
};
// One-off listener that updates the build floater UI when the prim inventory updates
class PBRPickerObjectListener : public LLVOInventoryListener
{
protected:
LLViewerObject* mObjectp;
bool mChangePending = true;
public:
PBRPickerObjectListener(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;
}
~PBRPickerObjectListener()
{
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;
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
LLUUID pbr_id;
if (mPBRTextureCtrl)
{
LLSelectedTE::getPbrMaterialId(pbr_id, identical_pbr, has_pbr_material, has_faces_without_pbr);
mPBRTextureCtrl->setTentative(!identical_pbr);
mPBRTextureCtrl->setEnabled(settable);
mPBRTextureCtrl->setImageAssetID(pbr_id);
if (objectp->isAttachment())
{
mPBRTextureCtrl->setFilterPermissionMasks(PERM_COPY | PERM_TRANSFER | PERM_MODIFY);
}
else
{
mPBRTextureCtrl->setImmediateFilterPermMask(PERM_NONE);
}
}
//mBtnPbrFromInv->setEnabled(settable); // <FS> Done via texture picker
mBtnEditBbr->setEnabled(editable && !has_faces_without_pbr);
mBtnSaveBbr->setEnabled(saveable && identical_pbr);
if (objectp->isInventoryPending())
{
// Reuse the same listener when possible
if (!mVOInventoryListener || !mVOInventoryListener->isListeningFor(objectp))
{
mVOInventoryListener = std::make_unique<PBRPickerObjectListener>(objectp);
}
}
else
{
mVOInventoryListener = nullptr;
}
if (!identical_pbr || pbr_id.isNull() || pbr_id == BLANK_MATERIAL_ASSET_ID)
{
mAgentInventoryListener = nullptr;
}
else
{
if (!mAgentInventoryListener || !mAgentInventoryListener->isListening())
{
mAgentInventoryListener = std::make_unique<PBRPickerAgentListener>();
}
}
const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled();
if (show_pbr)
{
const bool new_state = has_pbr_capabilities && has_pbr_material && !has_faces_without_pbr;
mPBRScaleU->setEnabled(new_state);
mPBRScaleV->setEnabled(new_state);
mPBRRotate->setEnabled(new_state);
mPBROffsetU->setEnabled(new_state);
mPBROffsetV->setEnabled(new_state);
// Control values will be set once per frame in
// setMaterialOverridesFromSelection
sMaterialOverrideSelection.setDirty();
}
}
void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */)
{
const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled();
const bool inventory_pending = objectp && objectp->isInventoryPending();
mRadioPbrType->setVisible(show_pbr);
const U32 pbr_type = mRadioPbrType->getSelectedIndex();
const bool show_pbr_render_material_id = show_pbr && (pbr_type == PBRTYPE_RENDER_MATERIAL_ID);
mPBRTextureCtrl->setVisible(show_pbr_render_material_id);
//mBtnPbrFromInv->setVisible(show_pbr_render_material_id); // <FS> Done via texture picker
mBtnEditBbr->setVisible(show_pbr_render_material_id && !inventory_pending);
mBtnSaveBbr->setVisible(show_pbr_render_material_id && !inventory_pending);
mLabelMatPermLoading->setVisible(show_pbr_render_material_id && inventory_pending);
mPBRScaleU->setVisible(show_pbr);
mPBRScaleV->setVisible(show_pbr);
mPBRRotate->setVisible(show_pbr);
mPBROffsetU->setVisible(show_pbr);
mPBROffsetV->setVisible(show_pbr);
}
void LLPanelFace::updateCopyTexButton()
{
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
// <FS> Extended copy & paste buttons
//mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify()
// && !objectp->isPermanentEnforced() && !objectp->isInventoryPending()
// && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)
// && LLMaterialEditor::canClipboardObjectsMaterial()
// && !mExcludeWater);
//std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options");
//mMenuClipboardTexture->setToolTip(tooltip);
mBtnCopyFaces->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify()
&& !objectp->isPermanentEnforced() && !objectp->isInventoryPending()
&& (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)
&& LLMaterialEditor::canClipboardObjectsMaterial()
&& !mExcludeWater);
std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options");
mBtnCopyFaces->setToolTip(tooltip);
// </FS>
}
void LLPanelFace::refresh()
{
LL_DEBUGS("Materials") << LL_ENDL;
getState();
}
void LLPanelFace::refreshMedia()
{
LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
LLViewerObject* first_object = selected_objects->getFirstObject();
if (!(first_object
&& first_object->getPCode() == LL_PCODE_VOLUME
&& first_object->permModify()
))
{
mAddMedia->setEnabled(false);
mTitleMediaText->clear();
clearMediaSettings();
return;
}
std::string url = first_object->getRegion()->getCapability("ObjectMedia");
bool has_media_capability = (!url.empty());
if (!has_media_capability)
{
mAddMedia->setEnabled(false);
LL_WARNS("LLFloaterToolsMedia") << "Media not enabled (no capability) in this region!" << LL_ENDL;
clearMediaSettings();
return;
}
bool is_nonpermanent_enforced = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode()
&& LLSelectMgr::getInstance()->selectGetRootsNonPermanentEnforced())
|| LLSelectMgr::getInstance()->selectGetNonPermanentEnforced();
bool editable = is_nonpermanent_enforced && (first_object->permModify() || selectedMediaEditable());
// Check modify permissions and whether any selected objects are in
// the process of being fetched. If they are, then we're not editable
if (editable)
{
LLObjectSelection::iterator iter = selected_objects->begin();
LLObjectSelection::iterator end = selected_objects->end();
for (; iter != end; ++iter)
{
LLSelectNode* node = *iter;
LLVOVolume* object = dynamic_cast<LLVOVolume*>(node->getObject());
if (NULL != object)
{
if (!object->permModify())
{
LL_INFOS("LLFloaterToolsMedia")
<< "Selection not editable due to lack of modify permissions on object id "
<< object->getID() << LL_ENDL;
editable = false;
break;
}
}
}
}
// Media settings
bool bool_has_media = false;
struct media_functor : public LLSelectedTEGetFunctor<bool>
{
bool get(LLViewerObject* object, S32 face)
{
LLTextureEntry *te = object->getTE(face);
if (te)
{
return te->hasMedia();
}
return false;
}
} func;
// check if all faces have media(or, all dont have media)
LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue(&func, bool_has_media);
const LLMediaEntry default_media_data;
struct functor_getter_media_data : public LLSelectedTEGetFunctor< LLMediaEntry>
{
functor_getter_media_data(const LLMediaEntry& entry) : mMediaEntry(entry) {}
LLMediaEntry get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return *(object->getTE(face)->getMediaData());
return mMediaEntry;
};
const LLMediaEntry& mMediaEntry;
} func_media_data(default_media_data);
LLMediaEntry media_data_get;
LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
std::string multi_media_info_str = LLTrans::getString("Multiple Media");
std::string media_title = "";
// update UI depending on whether "object" (prim or face) has media
// and whether or not you are allowed to edit it.
mAddMedia->setEnabled(editable);
// IF all the faces have media (or all dont have media)
if (LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo)
{
// TODO: get media title and set it.
mTitleMediaText->clear();
// if identical is set, all faces are same (whether all empty or has the same media)
if (!(LLFloaterMediaSettings::getInstance()->mMultipleMedia))
{
// Media data is valid
if (media_data_get != default_media_data)
{
// initial media title is the media URL (until we get the name)
media_title = media_data_get.getHomeURL();
}
// else all faces might be empty.
}
else // there' re Different Medias' been set on on the faces.
{
media_title = multi_media_info_str;
}
mDelMedia->setEnabled(bool_has_media && editable);
// TODO: display a list of all media on the face - use 'identical' flag
}
else // not all face has media but at least one does.
{
// seleted faces have not identical value
LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
if (LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
{
media_title = multi_media_info_str;
}
else
{
// Media data is valid
if (media_data_get != default_media_data)
{
// initial media title is the media URL (until we get the name)
media_title = media_data_get.getHomeURL();
}
}
mDelMedia->setEnabled(true);
}
U32 materials_media = mComboMatMedia->getCurrentIndex();
if (materials_media == MATMEDIA_MEDIA)
{
// currently displaying media info, navigateTo and update title
navigateToTitleMedia(media_title);
}
else
{
// Media can be heavy, don't keep it around
// MAC specific: MAC doesn't support setVolume(0) so if not
// unloaded, it might keep playing audio until user closes editor
unloadMedia();
mNeedMediaTitle = false;
}
mTitleMediaText->setText(media_title);
// load values for media settings
updateMediaSettings();
LLFloaterMediaSettings::initValues(mMediaSettings, editable);
}
void LLPanelFace::unloadMedia()
{
// destroy media source used to grab media title
if (mTitleMedia)
mTitleMedia->unloadMediaSource();
}
// static
void LLPanelFace::onMaterialOverrideReceived(const LLUUID& object_id, S32 side)
{
sMaterialOverrideSelection.onSelectedObjectUpdated(object_id, side);
}
//////////////////////////////////////////////////////////////////////////////
//
void LLPanelFace::navigateToTitleMedia(const std::string& url)
{
std::string multi_media_info_str = LLTrans::getString("Multiple Media");
if (url.empty() || multi_media_info_str == url)
{
// nothing to show
mNeedMediaTitle = false;
}
else if (mTitleMedia)
{
LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin();
// check if url changed or if we need a new media source
if (mTitleMedia->getCurrentNavUrl() != url || media_plugin == nullptr)
{
mTitleMedia->navigateTo(url);
LLViewerMediaImpl* impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(mTitleMedia->getTextureID());
if (impl)
{
// if it's a page with a movie, we don't want to hear it
impl->setVolume(0);
};
}
// flag that we need to update the title (even if no request were made)
mNeedMediaTitle = true;
}
}
bool LLPanelFace::selectedMediaEditable()
{
U32 owner_mask_on;
U32 owner_mask_off;
U32 valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER,
&owner_mask_on, &owner_mask_off);
U32 group_mask_on;
U32 group_mask_off;
U32 valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_GROUP,
&group_mask_on, &group_mask_off);
U32 everyone_mask_on;
U32 everyone_mask_off;
S32 valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_EVERYONE,
&everyone_mask_on, &everyone_mask_off);
bool selected_Media_editable = false;
// if perms we got back are valid
if (valid_owner_perms &&
valid_group_perms &&
valid_everyone_perms)
{
if ((owner_mask_on & PERM_MODIFY) ||
(group_mask_on & PERM_MODIFY) ||
(everyone_mask_on & PERM_MODIFY))
{
selected_Media_editable = true;
}
else
// user is NOT allowed to press the RESET button
{
selected_Media_editable = false;
};
};
return selected_Media_editable;
}
void LLPanelFace::clearMediaSettings()
{
LLFloaterMediaSettings::clearValues(false);
}
void LLPanelFace::updateMediaSettings()
{
bool identical(false);
std::string base_key("");
std::string value_str("");
int value_int = 0;
bool value_bool = false;
LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
// TODO: (CP) refactor this using something clever or boost or both !!
const LLMediaEntry default_media_data;
// controls
U8 value_u8 = default_media_data.getControls();
struct functor_getter_controls : public LLSelectedTEGetFunctor< U8 >
{
functor_getter_controls(const LLMediaEntry &entry) : mMediaEntry(entry) {}
U8 get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getControls();
return mMediaEntry.getControls();
};
const LLMediaEntry &mMediaEntry;
} func_controls(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_controls, value_u8);
base_key = std::string(LLMediaEntry::CONTROLS_KEY);
mMediaSettings[base_key] = value_u8;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// First click (formerly left click)
value_bool = default_media_data.getFirstClickInteract();
struct functor_getter_first_click : public LLSelectedTEGetFunctor< bool >
{
functor_getter_first_click(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getFirstClickInteract();
return mMediaEntry.getFirstClickInteract();
};
const LLMediaEntry &mMediaEntry;
} func_first_click(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_first_click, value_bool);
base_key = std::string(LLMediaEntry::FIRST_CLICK_INTERACT_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Home URL
value_str = default_media_data.getHomeURL();
struct functor_getter_home_url : public LLSelectedTEGetFunctor< std::string >
{
functor_getter_home_url(const LLMediaEntry& entry) : mMediaEntry(entry) {}
std::string get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getHomeURL();
return mMediaEntry.getHomeURL();
};
const LLMediaEntry &mMediaEntry;
} func_home_url(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_home_url, value_str);
base_key = std::string(LLMediaEntry::HOME_URL_KEY);
mMediaSettings[base_key] = value_str;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Current URL
value_str = default_media_data.getCurrentURL();
struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string >
{
functor_getter_current_url(const LLMediaEntry& entry) : mMediaEntry(entry) {}
std::string get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getCurrentURL();
return mMediaEntry.getCurrentURL();
};
const LLMediaEntry &mMediaEntry;
} func_current_url(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_current_url, value_str);
base_key = std::string(LLMediaEntry::CURRENT_URL_KEY);
mMediaSettings[base_key] = value_str;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Auto zoom
value_bool = default_media_data.getAutoZoom();
struct functor_getter_auto_zoom : public LLSelectedTEGetFunctor< bool >
{
functor_getter_auto_zoom(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getAutoZoom();
return mMediaEntry.getAutoZoom();
};
const LLMediaEntry &mMediaEntry;
} func_auto_zoom(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_auto_zoom, value_bool);
base_key = std::string(LLMediaEntry::AUTO_ZOOM_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Auto play
//value_bool = default_media_data.getAutoPlay();
// set default to auto play true -- angela EXT-5172
value_bool = true;
struct functor_getter_auto_play : public LLSelectedTEGetFunctor< bool >
{
functor_getter_auto_play(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getAutoPlay();
//return mMediaEntry.getAutoPlay(); set default to auto play true -- angela EXT-5172
return true;
};
const LLMediaEntry &mMediaEntry;
} func_auto_play(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_auto_play, value_bool);
base_key = std::string(LLMediaEntry::AUTO_PLAY_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Auto scale
// set default to auto scale true -- angela EXT-5172
//value_bool = default_media_data.getAutoScale();
value_bool = true;
struct functor_getter_auto_scale : public LLSelectedTEGetFunctor< bool >
{
functor_getter_auto_scale(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getAutoScale();
// return mMediaEntry.getAutoScale(); set default to auto scale true -- angela EXT-5172
return true;
};
const LLMediaEntry &mMediaEntry;
} func_auto_scale(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_auto_scale, value_bool);
base_key = std::string(LLMediaEntry::AUTO_SCALE_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Auto loop
value_bool = default_media_data.getAutoLoop();
struct functor_getter_auto_loop : public LLSelectedTEGetFunctor< bool >
{
functor_getter_auto_loop(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getAutoLoop();
return mMediaEntry.getAutoLoop();
};
const LLMediaEntry &mMediaEntry;
} func_auto_loop(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_auto_loop, value_bool);
base_key = std::string(LLMediaEntry::AUTO_LOOP_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// width pixels (if not auto scaled)
value_int = default_media_data.getWidthPixels();
struct functor_getter_width_pixels : public LLSelectedTEGetFunctor< int >
{
functor_getter_width_pixels(const LLMediaEntry& entry) : mMediaEntry(entry) {}
int get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getWidthPixels();
return mMediaEntry.getWidthPixels();
};
const LLMediaEntry &mMediaEntry;
} func_width_pixels(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_width_pixels, value_int);
base_key = std::string(LLMediaEntry::WIDTH_PIXELS_KEY);
mMediaSettings[base_key] = value_int;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// height pixels (if not auto scaled)
value_int = default_media_data.getHeightPixels();
struct functor_getter_height_pixels : public LLSelectedTEGetFunctor< int >
{
functor_getter_height_pixels(const LLMediaEntry& entry) : mMediaEntry(entry) {}
int get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getHeightPixels();
return mMediaEntry.getHeightPixels();
};
const LLMediaEntry &mMediaEntry;
} func_height_pixels(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_height_pixels, value_int);
base_key = std::string(LLMediaEntry::HEIGHT_PIXELS_KEY);
mMediaSettings[base_key] = value_int;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Enable Alt image
value_bool = default_media_data.getAltImageEnable();
struct functor_getter_enable_alt_image : public LLSelectedTEGetFunctor< bool >
{
functor_getter_enable_alt_image(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getAltImageEnable();
return mMediaEntry.getAltImageEnable();
};
const LLMediaEntry &mMediaEntry;
} func_enable_alt_image(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_enable_alt_image, value_bool);
base_key = std::string(LLMediaEntry::ALT_IMAGE_ENABLE_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - owner interact
value_bool = 0 != (default_media_data.getPermsInteract() & LLMediaEntry::PERM_OWNER);
struct functor_getter_perms_owner_interact : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_owner_interact(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_OWNER));
return 0 != (mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_OWNER);
};
const LLMediaEntry &mMediaEntry;
} func_perms_owner_interact(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_perms_owner_interact, value_bool);
base_key = std::string(LLPanelContents::PERMS_OWNER_INTERACT_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - owner control
value_bool = 0 != (default_media_data.getPermsControl() & LLMediaEntry::PERM_OWNER);
struct functor_getter_perms_owner_control : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_owner_control(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_OWNER));
return 0 != (mMediaEntry.getPermsControl() & LLMediaEntry::PERM_OWNER);
};
const LLMediaEntry &mMediaEntry;
} func_perms_owner_control(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_perms_owner_control, value_bool);
base_key = std::string(LLPanelContents::PERMS_OWNER_CONTROL_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - group interact
value_bool = 0 != (default_media_data.getPermsInteract() & LLMediaEntry::PERM_GROUP);
struct functor_getter_perms_group_interact : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_group_interact(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_GROUP));
return 0 != (mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_GROUP);
};
const LLMediaEntry &mMediaEntry;
} func_perms_group_interact(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_perms_group_interact, value_bool);
base_key = std::string(LLPanelContents::PERMS_GROUP_INTERACT_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - group control
value_bool = 0 != (default_media_data.getPermsControl() & LLMediaEntry::PERM_GROUP);
struct functor_getter_perms_group_control : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_group_control(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_GROUP));
return 0 != (mMediaEntry.getPermsControl() & LLMediaEntry::PERM_GROUP);
};
const LLMediaEntry &mMediaEntry;
} func_perms_group_control(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_perms_group_control, value_bool);
base_key = std::string(LLPanelContents::PERMS_GROUP_CONTROL_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - anyone interact
value_bool = 0 != (default_media_data.getPermsInteract() & LLMediaEntry::PERM_ANYONE);
struct functor_getter_perms_anyone_interact : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_anyone_interact(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_ANYONE));
return 0 != (mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_ANYONE);
};
const LLMediaEntry &mMediaEntry;
} func_perms_anyone_interact(default_media_data);
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func_perms_anyone_interact, value_bool);
base_key = std::string(LLPanelContents::PERMS_ANYONE_INTERACT_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// Perms - anyone control
value_bool = 0 != (default_media_data.getPermsControl() & LLMediaEntry::PERM_ANYONE);
struct functor_getter_perms_anyone_control : public LLSelectedTEGetFunctor< bool >
{
functor_getter_perms_anyone_control(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_ANYONE));
return 0 != (mMediaEntry.getPermsControl() & LLMediaEntry::PERM_ANYONE);
};
const LLMediaEntry &mMediaEntry;
} func_perms_anyone_control(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_perms_anyone_control, value_bool);
base_key = std::string(LLPanelContents::PERMS_ANYONE_CONTROL_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// security - whitelist enable
value_bool = default_media_data.getWhiteListEnable();
struct functor_getter_whitelist_enable : public LLSelectedTEGetFunctor< bool >
{
functor_getter_whitelist_enable(const LLMediaEntry& entry) : mMediaEntry(entry) {}
bool get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getWhiteListEnable();
return mMediaEntry.getWhiteListEnable();
};
const LLMediaEntry &mMediaEntry;
} func_whitelist_enable(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_whitelist_enable, value_bool);
base_key = std::string(LLMediaEntry::WHITELIST_ENABLE_KEY);
mMediaSettings[base_key] = value_bool;
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
// security - whitelist URLs
std::vector<std::string> value_vector_str = default_media_data.getWhiteList();
struct functor_getter_whitelist_urls : public LLSelectedTEGetFunctor< std::vector<std::string> >
{
functor_getter_whitelist_urls(const LLMediaEntry& entry) : mMediaEntry(entry) {}
std::vector<std::string> get(LLViewerObject* object, S32 face)
{
if (object)
if (object->getTE(face))
if (object->getTE(face)->getMediaData())
return object->getTE(face)->getMediaData()->getWhiteList();
return mMediaEntry.getWhiteList();
};
const LLMediaEntry &mMediaEntry;
} func_whitelist_urls(default_media_data);
identical = selected_objects->getSelectedTEValue(&func_whitelist_urls, value_vector_str);
base_key = std::string(LLMediaEntry::WHITELIST_KEY);
mMediaSettings[base_key].clear();
std::vector< std::string >::iterator iter = value_vector_str.begin();
while (iter != value_vector_str.end())
{
std::string white_list_url = *iter;
mMediaSettings[base_key].append(white_list_url);
++iter;
};
mMediaSettings[base_key + std::string(LLPanelContents::TENTATIVE_SUFFIX)] = !identical;
}
void LLPanelFace::updateMediaTitle()
{
// only get the media name if we need it
if (!mNeedMediaTitle)
return;
// get plugin impl
LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin();
if (media_plugin && mTitleMedia->getCurrentNavUrl() == media_plugin->getNavigateURI())
{
// get the media name (asynchronous - must call repeatedly)
std::string media_title = media_plugin->getMediaName();
// only replace the title if what we get contains something
if (!media_title.empty())
{
// update the UI widget
if (mTitleMediaText)
{
mTitleMediaText->setText(media_title);
// stop looking for a title when we get one
mNeedMediaTitle = false;
};
};
};
}
// static
F32 LLPanelFace::valueGlow(LLViewerObject* object, S32 face)
{
return (F32)(object->getTEref(face).getGlow());
}
void LLPanelFace::onCommitColor()
{
sendColor();
}
void LLPanelFace::onCommitShinyColor()
{
LLSelectedTEMaterial::setSpecularLightColor(this, mShinyColorSwatch->get());
}
void LLPanelFace::onCommitAlpha()
{
sendAlpha();
}
void LLPanelFace::onCancelColor()
{
LLSelectMgr::getInstance()->selectionRevertColors();
}
void LLPanelFace::onCancelShinyColor()
{
LLSelectMgr::getInstance()->selectionRevertShinyColors();
}
void LLPanelFace::onSelectColor()
{
LLSelectMgr::getInstance()->saveSelectedObjectColors();
sendColor();
}
void LLPanelFace::onSelectShinyColor()
{
LLSelectedTEMaterial::setSpecularLightColor(this, mShinyColorSwatch->get());
LLSelectMgr::getInstance()->saveSelectedShinyColors();
}
void LLPanelFace::onCommitMaterialsMedia()
{
// Force to default states to side-step problems with menu contents
// and generally reflecting old state when switching tabs or objects
//
updateShinyControls(false, true);
updateBumpyControls(false, true);
updateUI();
refreshMedia();
}
void LLPanelFace::updateVisibility(LLViewerObject* objectp /* = nullptr */)
{
if (!mRadioMaterialType || !mRadioPbrType)
{
LL_WARNS("Materials") << "Combo box not found...exiting." << LL_ENDL;
return;
}
U32 materials_media = mComboMatMedia->getCurrentIndex();
U32 material_type = mRadioMaterialType->getSelectedIndex();
bool show_media = (materials_media == MATMEDIA_MEDIA) && mComboMatMedia->getEnabled();
bool show_material = materials_media == MATMEDIA_MATERIAL;
bool show_texture = (show_media || (show_material && (material_type == MATTYPE_DIFFUSE) && mComboMatMedia->getEnabled()));
bool show_bumpiness = show_material && (material_type == MATTYPE_NORMAL) && mComboMatMedia->getEnabled();
bool show_shininess = show_material && (material_type == MATTYPE_SPECULAR) && mComboMatMedia->getEnabled();
const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled();
const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo();
const bool show_pbr_asset = show_pbr && texture_info == LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT;
mRadioMaterialType->setVisible(show_material);
// Shared material controls
mCheckSyncSettings->setVisible(show_material || show_media);
mLabelTexGen->setVisible(show_material || show_media || show_pbr_asset);
mComboTexGen->setVisible(show_material || show_media || show_pbr_asset);
mBtnAlignTex->setVisible(show_material || show_media);
// <FS:CR> FIRE-11407 - Be consistant and hide this with the other controls
mPlanarAlign->setVisible((show_material || show_media) && !show_pbr);
mBtnTexFlipScaleU->setVisible((show_material || show_media) && !show_pbr);
mBtnTexFlipScaleU->setVisible((show_material || show_media) && !show_pbr);
// </FS:CR>
// Media controls
mTitleMediaText->setVisible(show_media);
mAddMedia->setVisible(show_media);
mDelMedia->setVisible(show_media);
mBtnAlign->setVisible(show_media);
// Diffuse texture controls
mTextureCtrl->setVisible(show_texture && show_material);
mLabelAlphaMode->setVisible(show_texture && show_material);
mComboAlphaMode->setVisible(show_texture && show_material);
mLabelMaskCutoff->setVisible(false);
mMaskCutoff->setVisible(false);
if (show_texture && show_material)
{
updateAlphaControls();
}
// texture scale and position controls
mTexScaleU->setVisible(show_texture);
mTexScaleV->setVisible(show_texture);
mTexRotate->setVisible(show_texture);
mTexOffsetU->setVisible(show_texture);
mTexOffsetV->setVisible(show_texture);
// Specular map controls
mShinyTextureCtrl->setVisible(show_shininess);
mComboShininess->setVisible(show_shininess);
mLabelShininess->setVisible(show_shininess);
mLabelGlossiness->setVisible(false);
mGlossiness->setVisible(false);
mLabelEnvironment->setVisible(false);
mEnvironment->setVisible(false);
mLabelShiniColor->setVisible(false);
mShinyColorSwatch->setVisible(false);
if (show_shininess)
{
updateShinyControls();
}
mShinyScaleU->setVisible(show_shininess);
mShinyScaleV->setVisible(show_shininess);
mShinyRotate->setVisible(show_shininess);
mShinyOffsetU->setVisible(show_shininess);
mShinyOffsetV->setVisible(show_shininess);
// Normal map controls
if (show_bumpiness)
{
updateBumpyControls();
}
mBumpyTextureCtrl->setVisible(show_bumpiness);
mComboBumpiness->setVisible(show_bumpiness);
mLabelBumpiness->setVisible(show_bumpiness);
mBumpyScaleU->setVisible(show_bumpiness);
mBumpyScaleV->setVisible(show_bumpiness);
mBumpyRotate->setVisible(show_bumpiness);
mBumpyOffsetU->setVisible(show_bumpiness);
mBumpyOffsetV->setVisible(show_bumpiness);
mTexRepeat->setVisible(show_material || show_media);
// PBR controls
updateVisibilityGLTF(objectp);
// <FS:Zi> Find all faces with same texture
mBtnSelectSameDiff->setVisible(mTextureCtrl->getVisible());
mBtnSelectSameNorm->setVisible(mBumpyTextureCtrl->getVisible());
mBtnSelectSameSpec->setVisible(mShinyTextureCtrl->getVisible());
// </FS:Zi>
}
void LLPanelFace::onCommitMaterialType()
{
// Force to default states to side-step problems with menu contents
// and generally reflecting old state when switching tabs or objects
//
updateShinyControls(false, true);
updateBumpyControls(false, true);
updateUI();
}
void LLPanelFace::onCommitPbrType()
{
// Force to default states to side-step problems with menu contents
// and generally reflecting old state when switching tabs or objects
//
updateUI();
}
void LLPanelFace::onCommitBump()
{
sendBump(mComboBumpiness->getCurrentIndex());
}
void LLPanelFace::onCommitTexGen()
{
sendTexGen();
}
void LLPanelFace::updateShinyControls(bool is_setting_texture, bool mess_with_shiny_combobox)
{
LLUUID shiny_texture_ID = mShinyTextureCtrl->getImageAssetID();
LL_DEBUGS("Materials") << "Shiny texture selected: " << shiny_texture_ID << LL_ENDL;
if (mess_with_shiny_combobox)
{
if (!shiny_texture_ID.isNull() && is_setting_texture)
{
if (!mComboShininess->itemExists(USE_TEXTURE))
{
mComboShininess->add(USE_TEXTURE);
}
mComboShininess->setSimple(USE_TEXTURE);
}
else
{
if (mComboShininess->itemExists(USE_TEXTURE))
{
mComboShininess->remove(SHINY_TEXTURE);
mComboShininess->selectFirstItem();
}
}
}
else
{
if (shiny_texture_ID.isNull() && mComboShininess->itemExists(USE_TEXTURE))
{
mComboShininess->remove(SHINY_TEXTURE);
mComboShininess->selectFirstItem();
}
}
U32 materials_media = mComboMatMedia->getCurrentIndex();
U32 material_type = mRadioMaterialType->getSelectedIndex();
bool show_material = (materials_media == MATMEDIA_MATERIAL);
bool show_shininess = show_material && (material_type == MATTYPE_SPECULAR) && mComboMatMedia->getEnabled();
U32 shiny_value = mComboShininess->getCurrentIndex();
bool show_shinyctrls = (shiny_value == SHINY_TEXTURE) && show_shininess; // Use texture
mLabelGlossiness->setVisible(show_shinyctrls);
mGlossiness->setVisible(show_shinyctrls);
mLabelEnvironment->setVisible(show_shinyctrls);
mEnvironment->setVisible(show_shinyctrls);
mLabelShiniColor->setVisible(show_shinyctrls);
mShinyColorSwatch->setVisible(show_shinyctrls);
}
void LLPanelFace::updateBumpyControls(bool is_setting_texture, bool mess_with_combobox)
{
LLUUID bumpy_texture_ID = mBumpyTextureCtrl->getImageAssetID();
LL_DEBUGS("Materials") << "texture: " << bumpy_texture_ID << (mess_with_combobox ? "" : " do not") << " update combobox" << LL_ENDL;
if (mess_with_combobox)
{
if (!bumpy_texture_ID.isNull() && is_setting_texture)
{
if (!mComboBumpiness->itemExists(USE_TEXTURE))
{
mComboBumpiness->add(USE_TEXTURE);
}
mComboBumpiness->setSimple(USE_TEXTURE);
}
else
{
if (mComboBumpiness->itemExists(USE_TEXTURE))
{
mComboBumpiness->remove(BUMPY_TEXTURE);
mComboBumpiness->selectFirstItem();
}
}
}
}
void LLPanelFace::onCommitShiny()
{
sendShiny(mComboShininess->getCurrentIndex());
}
void LLPanelFace::updateAlphaControls()
{
U32 alpha_value = mComboAlphaMode->getCurrentIndex();
bool show_alphactrls = (alpha_value == ALPHAMODE_MASK); // Alpha masking
U32 mat_media = mComboMatMedia->getCurrentIndex();
U32 mat_type = mRadioMaterialType->getSelectedIndex();
show_alphactrls = show_alphactrls && (mat_media == MATMEDIA_MATERIAL);
show_alphactrls = show_alphactrls && (mat_type == MATTYPE_DIFFUSE);
mLabelMaskCutoff->setVisible(show_alphactrls);
mMaskCutoff->setVisible(show_alphactrls);
}
void LLPanelFace::onCommitAlphaMode()
{
updateAlphaControls();
LLSelectedTEMaterial::setDiffuseAlphaMode(this, getCurrentDiffuseAlphaMode());
}
void LLPanelFace::onCommitFullbright()
{
sendFullbright();
}
void LLPanelFace::onCommitHideWater()
{
if (mCheckHideWater->get())
{
LLHandle<LLPanel> handle = getHandle();
LLNotificationsUtil::add("WaterExclusionSurfacesWarning", LLSD(), LLSD(),
[handle](const LLSD& notification, const LLSD& response)
{
if(LLPanelFace* panel = (LLPanelFace*)handle.get())
{
if (LLNotificationsUtil::getSelectedOption(notification, response) == 1)
{
panel->mCheckHideWater->setValue(false);
return;
}
// apply invisiprim texture and reset related params to set water exclusion surface
panel->sendBump(0);
panel->sendShiny(0);
LLSelectMgr::getInstance()->selectionSetAlphaOnly(1.f);
LLSelectMgr::getInstance()->selectionSetImage(IMG_ALPHA_GRAD);
LLSelectedTEMaterial::setDiffuseAlphaMode(panel, LLMaterial::DIFFUSE_ALPHA_MODE_BLEND);
}
});
}
else
{
LLSelectMgr::getInstance()->clearWaterExclusion();
}
}
void LLPanelFace::onCommitGlow()
{
sendGlow();
}
bool LLPanelFace::onDragPbr(LLInventoryItem* item)
{
bool accept = true;
for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin();
iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++)
{
LLSelectNode* node = *iter;
LLViewerObject* obj = node->getObject();
if (!LLToolDragAndDrop::isInventoryDropAcceptable(obj, item))
{
accept = false;
break;
}
}
return accept;
}
void LLPanelFace::onCommitPbr()
{
if (!mPBRTextureCtrl->getTentative())
{
// we grab the item id first, because we want to do a
// permissions check in the selection manager. ARGH!
LLUUID id = mPBRTextureCtrl->getImageItemID();
if (id.isNull())
{
id = mPBRTextureCtrl->getImageAssetID();
}
if (!LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id))
{
// If failed to set material, refresh mPBRTextureCtrl's value
refresh();
}
}
}
void LLPanelFace::onCancelPbr()
{
LLSelectMgr::getInstance()->selectionRevertGLTFMaterials();
}
void LLPanelFace::onSelectPbr()
{
LLSelectMgr::getInstance()->saveSelectedObjectTextures();
if (!mPBRTextureCtrl->getTentative())
{
// we grab the item id first, because we want to do a
// permissions check in the selection manager. ARGH!
LLUUID id = mPBRTextureCtrl->getImageItemID();
if (id.isNull())
{
id = mPBRTextureCtrl->getImageAssetID();
}
if (!LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id))
{
refresh();
}
}
}
bool LLPanelFace::onDragTexture(LLInventoryItem* item)
{
bool accept = true;
for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin();
iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++)
{
LLSelectNode* node = *iter;
LLViewerObject* obj = node->getObject();
if (!LLToolDragAndDrop::isInventoryDropAcceptable(obj, item))
{
accept = false;
break;
}
}
return accept;
}
void LLPanelFace::onCommitTexture()
{
add(LLStatViewer::EDIT_TEXTURE, 1);
sendTexture();
}
void LLPanelFace::onCancelTexture()
{
LLSelectMgr::getInstance()->selectionRevertTextures();
}
void LLPanelFace::onSelectTexture()
{
LLSelectMgr::getInstance()->saveSelectedObjectTextures();
sendTexture();
LLGLenum image_format;
bool identical_image_format = false;
bool missing_asset = false;
LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);
U32 alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
if (!missing_asset)
{
switch (image_format)
{
case GL_RGBA:
case GL_ALPHA:
alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_BLEND;
break;
case GL_RGB:
break;
default:
LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL;
break;
}
mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode);
}
LLSelectedTEMaterial::setDiffuseAlphaMode(this, getCurrentDiffuseAlphaMode());
}
void LLPanelFace::onCloseTexturePicker(const LLSD& data)
{
LL_DEBUGS("Materials") << data << LL_ENDL;
updateUI();
}
void LLPanelFace::onCommitSpecularTexture(const LLSD& data)
{
LL_DEBUGS("Materials") << data << LL_ENDL;
sendShiny(SHINY_TEXTURE);
}
void LLPanelFace::onCommitNormalTexture(const LLSD& data)
{
LL_DEBUGS("Materials") << data << LL_ENDL;
LLUUID nmap_id = getCurrentNormalMap();
sendBump(nmap_id.isNull() ? 0 : BUMPY_TEXTURE);
}
void LLPanelFace::onCancelSpecularTexture(const LLSD& data)
{
U8 shiny = 0;
bool identical_shiny = false;
LLSelectedTE::getShiny(shiny, identical_shiny);
LLUUID spec_map_id = mShinyTextureCtrl->getImageAssetID();
shiny = spec_map_id.isNull() ? shiny : SHINY_TEXTURE;
sendShiny(shiny);
}
void LLPanelFace::onCancelNormalTexture(const LLSD& data)
{
U8 bumpy = 0;
bool identical_bumpy = false;
LLSelectedTE::getBumpmap(bumpy, identical_bumpy);
LLUUID spec_map_id = mBumpyTextureCtrl->getImageAssetID();
bumpy = spec_map_id.isNull() ? bumpy : BUMPY_TEXTURE;
sendBump(bumpy);
}
void LLPanelFace::onSelectSpecularTexture(const LLSD& data)
{
LL_DEBUGS("Materials") << data << LL_ENDL;
sendShiny(SHINY_TEXTURE);
}
void LLPanelFace::onSelectNormalTexture(const LLSD& data)
{
LL_DEBUGS("Materials") << data << LL_ENDL;
LLUUID nmap_id = getCurrentNormalMap();
sendBump(nmap_id.isNull() ? 0 : BUMPY_TEXTURE);
}
//////////////////////////////////////////////////////////////////////////////
// called when a user wants to edit existing media settings on a prim or prim face
// TODO: test if there is media on the item and only allow editing if present
void LLPanelFace::onClickBtnEditMedia()
{
refreshMedia();
LLFloaterReg::showInstance("media_settings");
}
//////////////////////////////////////////////////////////////////////////////
// called when a user wants to delete media from a prim or prim face
void LLPanelFace::onClickBtnDeleteMedia()
{
LLNotificationsUtil::add("DeleteMedia", LLSD(), LLSD(), deleteMediaConfirm);
}
//////////////////////////////////////////////////////////////////////////////
// called when a user wants to add media to a prim or prim face
void LLPanelFace::onClickBtnAddMedia()
{
// check if multiple faces are selected
if (LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected())
{
refreshMedia();
LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm);
}
else
{
onClickBtnEditMedia();
}
}
// static
bool LLPanelFace::deleteMediaConfirm(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
switch (option)
{
case 0: // "Yes"
LLSelectMgr::getInstance()->selectionSetMedia(0, LLSD());
if (LLFloaterReg::instanceVisible("media_settings"))
{
LLFloaterReg::hideInstance("media_settings");
}
break;
case 1: // "No"
default:
break;
}
return false;
}
// static
bool LLPanelFace::multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
switch (option)
{
case 0: // "Yes"
LLFloaterReg::showInstance("media_settings");
break;
case 1: // "No"
default:
break;
}
return false;
}
void LLPanelFace::syncOffsetX(F32 offsetU)
{
LLSelectedTEMaterial::setNormalOffsetX(this, offsetU);
LLSelectedTEMaterial::setSpecularOffsetX(this, offsetU);
mTexOffsetU->forceSetValue(LLSD(offsetU));
sendTextureInfo();
}
void LLPanelFace::syncOffsetY(F32 offsetV)
{
LLSelectedTEMaterial::setNormalOffsetY(this, offsetV);
LLSelectedTEMaterial::setSpecularOffsetY(this, offsetV);
mTexOffsetV->forceSetValue(LLSD(offsetV));
sendTextureInfo();
}
void LLPanelFace::onCommitMaterialBumpyOffsetX()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetX(getCurrentBumpyOffsetU());
}
else
{
LLSelectedTEMaterial::setNormalOffsetX(this, getCurrentBumpyOffsetU());
}
}
void LLPanelFace::onCommitMaterialBumpyOffsetY()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetY(getCurrentBumpyOffsetV());
}
else
{
LLSelectedTEMaterial::setNormalOffsetY(this, getCurrentBumpyOffsetV());
}
}
void LLPanelFace::onCommitMaterialShinyOffsetX()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetX(getCurrentShinyOffsetU());
}
else
{
LLSelectedTEMaterial::setSpecularOffsetX(this, getCurrentShinyOffsetU());
}
}
void LLPanelFace::onCommitMaterialShinyOffsetY()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetY(getCurrentShinyOffsetV());
}
else
{
LLSelectedTEMaterial::setSpecularOffsetY(this, getCurrentShinyOffsetV());
}
}
void LLPanelFace::syncRepeatX(F32 scaleU)
{
LLSelectedTEMaterial::setNormalRepeatX(this, scaleU);
LLSelectedTEMaterial::setSpecularRepeatX(this, scaleU);
sendTextureInfo();
}
void LLPanelFace::syncRepeatY(F32 scaleV)
{
LLSelectedTEMaterial::setNormalRepeatY(this, scaleV);
LLSelectedTEMaterial::setSpecularRepeatY(this, scaleV);
sendTextureInfo();
}
void LLPanelFace::onCommitMaterialBumpyScaleX()
{
F32 bumpy_scale_u = getCurrentBumpyScaleU();
if (isIdenticalPlanarTexgen())
{
bumpy_scale_u *= 0.5f;
}
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexScaleU->forceSetValue(LLSD(getCurrentBumpyScaleU()));
syncRepeatX(bumpy_scale_u);
}
else
{
LLSelectedTEMaterial::setNormalRepeatX(this, bumpy_scale_u);
}
}
void LLPanelFace::onCommitMaterialBumpyScaleY()
{
F32 bumpy_scale_v = getCurrentBumpyScaleV();
if (isIdenticalPlanarTexgen())
{
bumpy_scale_v *= 0.5f;
}
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexScaleV->forceSetValue(LLSD(getCurrentBumpyScaleV()));
syncRepeatY(bumpy_scale_v);
}
else
{
LLSelectedTEMaterial::setNormalRepeatY(this, bumpy_scale_v);
}
}
void LLPanelFace::onCommitMaterialShinyScaleX()
{
F32 shiny_scale_u = getCurrentShinyScaleU();
if (isIdenticalPlanarTexgen())
{
shiny_scale_u *= 0.5f;
}
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexScaleU->forceSetValue(LLSD(getCurrentShinyScaleU()));
syncRepeatX(shiny_scale_u);
}
else
{
LLSelectedTEMaterial::setSpecularRepeatX(this, shiny_scale_u);
}
}
void LLPanelFace::onCommitMaterialShinyScaleY()
{
F32 shiny_scale_v = getCurrentShinyScaleV();
if (isIdenticalPlanarTexgen())
{
shiny_scale_v *= 0.5f;
}
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexScaleV->forceSetValue(LLSD(getCurrentShinyScaleV()));
syncRepeatY(shiny_scale_v);
}
else
{
LLSelectedTEMaterial::setSpecularRepeatY(this, shiny_scale_v);
}
}
void LLPanelFace::syncMaterialRot(F32 rot, int te)
{
LLSelectedTEMaterial::setNormalRotation(this, rot * DEG_TO_RAD, te);
LLSelectedTEMaterial::setSpecularRotation(this, rot * DEG_TO_RAD, te);
sendTextureInfo();
}
void LLPanelFace::onCommitMaterialBumpyRot()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexRotate->forceSetValue(LLSD(getCurrentBumpyRot()));
syncMaterialRot(getCurrentBumpyRot());
}
else
{
if (mPlanarAlign->getValue().asBoolean())
{
LLFace* last_face = NULL;
bool identical_face = false;
LLSelectedTE::getFace(last_face, identical_face);
LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
}
else
{
LLSelectedTEMaterial::setNormalRotation(this, getCurrentBumpyRot() * DEG_TO_RAD);
}
}
}
void LLPanelFace::onCommitMaterialShinyRot()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
mTexRotate->forceSetValue(LLSD(getCurrentShinyRot()));
syncMaterialRot(getCurrentShinyRot());
}
else
{
if (mPlanarAlign->getValue().asBoolean())
{
LLFace* last_face = NULL;
bool identical_face = false;
LLSelectedTE::getFace(last_face, identical_face);
LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
}
else
{
LLSelectedTEMaterial::setSpecularRotation(this, getCurrentShinyRot() * DEG_TO_RAD);
}
}
}
void LLPanelFace::onCommitMaterialGloss()
{
LLSelectedTEMaterial::setSpecularLightExponent(this, getCurrentGlossiness());
}
void LLPanelFace::onCommitMaterialEnv()
{
LLSelectedTEMaterial::setEnvironmentIntensity(this, getCurrentEnvIntensity());
}
void LLPanelFace::onCommitMaterialMaskCutoff()
{
LLSelectedTEMaterial::setAlphaMaskCutoff(this, getCurrentAlphaMaskCutoff());
}
void LLPanelFace::onCommitTextureInfo()
{
sendTextureInfo();
// vertical scale and repeats per meter depends on each other, so force set on changes
updateUI(true);
}
void LLPanelFace::onCommitTextureScaleX()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
F32 bumpy_scale_u = (F32)mTexScaleU->getValue().asReal();
if (isIdenticalPlanarTexgen())
{
bumpy_scale_u *= 0.5f;
}
syncRepeatX(bumpy_scale_u);
}
else
{
sendTextureInfo();
}
updateUI(true);
}
void LLPanelFace::onCommitTextureScaleY()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
F32 bumpy_scale_v = (F32)mTexScaleV->getValue().asReal();
if (isIdenticalPlanarTexgen())
{
bumpy_scale_v *= 0.5f;
}
syncRepeatY(bumpy_scale_v);
}
else
{
sendTextureInfo();
}
updateUI(true);
}
void LLPanelFace::onCommitTextureRot()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncMaterialRot((F32)mTexRotate->getValue().asReal());
}
else
{
sendTextureInfo();
}
updateUI(true);
}
void LLPanelFace::onCommitTextureOffsetX()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetX((F32)mTexOffsetU->getValue().asReal());
}
else
{
sendTextureInfo();
}
updateUI(true);
}
void LLPanelFace::onCommitTextureOffsetY()
{
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
syncOffsetY((F32)mTexOffsetV->getValue().asReal());
}
else
{
sendTextureInfo();
}
updateUI(true);
}
// Commit the number of repeats per meter
void LLPanelFace::onCommitRepeatsPerMeter()
{
F32 repeats_per_meter = (F32)mTexRepeat->getValue().asReal();
F32 obj_scale_s = 1.0f;
F32 obj_scale_t = 1.0f;
bool identical_scale_s = false;
bool identical_scale_t = false;
LLSelectedTE::getObjectScaleS(obj_scale_s, identical_scale_s);
LLSelectedTE::getObjectScaleT(obj_scale_t, identical_scale_t);
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
mShinyScaleU->setValue(obj_scale_s * repeats_per_meter);
mShinyScaleV->setValue(obj_scale_t * repeats_per_meter);
LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
}
else
{
U32 material_type = mRadioMaterialType->getSelectedIndex();
switch (material_type)
{
case MATTYPE_DIFFUSE:
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
break;
case MATTYPE_NORMAL:
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
break;
case MATTYPE_SPECULAR:
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
break;
default:
llassert(false);
break;
}
}
// vertical scale and repeats per meter depends on each other, so force set on changes
updateUI(true);
}
struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor
{
virtual bool apply(LLViewerObject* object, S32 te)
{
viewer_media_t pMediaImpl;
const LLTextureEntry &tep = object->getTEref(te);
if (const LLMediaEntry* mep = tep.hasMedia() ? tep.getMediaData() : NULL)
{
pMediaImpl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(mep->getMediaID());
}
if (pMediaImpl.isNull())
{
// If we didn't find face media for this face, check whether this face is showing parcel media.
pMediaImpl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(tep.getID());
}
if (pMediaImpl.notNull())
{
if (LLPluginClassMedia* media = pMediaImpl->getMediaPlugin())
{
S32 media_width = media->getWidth();
S32 media_height = media->getHeight();
S32 texture_width = media->getTextureWidth();
S32 texture_height = media->getTextureHeight();
F32 scale_s = (F32)media_width / (F32)texture_width;
F32 scale_t = (F32)media_height / (F32)texture_height;
// set scale and adjust offset
object->setTEScaleS(te, scale_s);
object->setTEScaleT(te, scale_t); // don't need to flip Y anymore since QT does this for us now.
object->setTEOffsetS(te, -( 1.0f - scale_s ) / 2.0f);
object->setTEOffsetT(te, -( 1.0f - scale_t ) / 2.0f);
}
}
return true;
};
};
void LLPanelFace::onClickAutoFix()
{
LLPanelFaceSetMediaFunctor setfunc;
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
LLPanelFaceSendFunctor sendfunc;
LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
}
void LLPanelFace::onAlignTexture()
{
alignTextureLayer();
}
void LLPanelFace::onClickBtnLoadInvPBR()
{
// Shouldn't this be "save to inventory?"
mPBRTextureCtrl->showPicker(true);
}
void LLPanelFace::onClickBtnEditPBR()
{
LLMaterialEditor::loadLive();
}
void LLPanelFace::onClickBtnSavePBR()
{
LLMaterialEditor::saveObjectsMaterialAs();
}
enum EPasteMode
{
PASTE_COLOR,
PASTE_TEXTURE
};
struct LLPanelFacePasteTexFunctor : public LLSelectedTEFunctor
{
LLPanelFacePasteTexFunctor(LLPanelFace* panel, EPasteMode mode) :
mPanelFace(panel), mMode(mode) {}
virtual bool apply(LLViewerObject* objectp, S32 te)
{
switch (mMode)
{
case PASTE_COLOR:
mPanelFace->onPasteColor(objectp, te);
break;
case PASTE_TEXTURE:
mPanelFace->onPasteTexture(objectp, te);
break;
}
return true;
}
private:
LLPanelFace *mPanelFace;
EPasteMode mMode;
};
struct LLPanelFaceUpdateFunctor : public LLSelectedObjectFunctor
{
LLPanelFaceUpdateFunctor(bool update_media)
: mUpdateMedia(update_media)
{}
virtual bool apply(LLViewerObject* object)
{
object->sendTEUpdate();
if (mUpdateMedia)
{
LLVOVolume *vo = dynamic_cast<LLVOVolume*>(object);
if (vo && vo->hasMedia())
{
vo->sendMediaDataUpdate();
}
}
return true;
}
private:
bool mUpdateMedia;
};
struct LLPanelFaceNavigateHomeFunctor : public LLSelectedTEFunctor
{
virtual bool apply(LLViewerObject* objectp, S32 te)
{
if (objectp && objectp->getTE(te))
{
LLTextureEntry* tep = objectp->getTE(te);
const LLMediaEntry *media_data = tep->getMediaData();
if (media_data)
{
if (media_data->getCurrentURL().empty() && media_data->getAutoPlay())
{
viewer_media_t media_impl =
LLViewerMedia::getInstance()->getMediaImplFromTextureID(tep->getMediaData()->getMediaID());
if (media_impl)
{
media_impl->navigateHome();
}
}
}
}
return true;
}
};
void LLPanelFace::onCopyColor()
{
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
if (!objectp || !node
|| objectp->getPCode() != LL_PCODE_VOLUME
|| !objectp->permModify()
|| objectp->isPermanentEnforced()
|| selected_count > 1)
{
return;
}
if (mClipboardParams.has("color"))
{
mClipboardParams["color"].clear();
}
else
{
mClipboardParams["color"] = LLSD::emptyArray();
}
std::map<LLUUID, LLUUID> asset_item_map;
// a way to resolve situations where source and target have different amount of faces
S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
mClipboardParams["color_all_tes"] = (num_tes != 1) || (LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool());
for (S32 te = 0; te < num_tes; ++te)
{
if (node->isTESelected(te))
{
LLTextureEntry* tep = objectp->getTE(te);
if (tep)
{
LLSD te_data;
// asLLSD() includes media
te_data["te"] = tep->asLLSD(); // Note: includes a lot more than just color/alpha/glow
mClipboardParams["color"].append(te_data);
}
}
}
}
void LLPanelFace::onPasteColor()
{
if (!mClipboardParams.has("color"))
{
return;
}
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
if (!objectp || !node
|| objectp->getPCode() != LL_PCODE_VOLUME
|| !objectp->permModify()
|| objectp->isPermanentEnforced()
|| selected_count > 1)
{
// not supposed to happen
LL_WARNS() << "Failed to paste color due to missing or wrong selection" << LL_ENDL;
return;
}
bool face_selection_mode = LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool();
LLSD &clipboard = mClipboardParams["color"]; // array
S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
S32 compare_tes = num_tes;
if (face_selection_mode)
{
compare_tes = 0;
for (S32 te = 0; te < num_tes; ++te)
{
if (node->isTESelected(te))
{
compare_tes++;
}
}
}
// we can copy if single face was copied in edit face mode or if face count matches
if (!((clipboard.size() == 1) && mClipboardParams["color_all_tes"].asBoolean())
&& compare_tes != clipboard.size())
{
LLSD notif_args;
if (face_selection_mode)
{
static std::string reason = getString("paste_error_face_selection_mismatch");
notif_args["REASON"] = reason;
}
else
{
static std::string reason = getString("paste_error_object_face_count_mismatch");
notif_args["REASON"] = reason;
}
LLNotificationsUtil::add("FacePasteFailed", notif_args);
return;
}
LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
LLPanelFacePasteTexFunctor paste_func(this, PASTE_COLOR);
selected_objects->applyToTEs(&paste_func);
LLPanelFaceUpdateFunctor sendfunc(false);
selected_objects->applyToObjects(&sendfunc);
}
void LLPanelFace::onPasteColor(LLViewerObject* objectp, S32 te)
{
LLSD te_data;
LLSD &clipboard = mClipboardParams["color"]; // array
if ((clipboard.size() == 1) && mClipboardParams["color_all_tes"].asBoolean())
{
te_data = *(clipboard.beginArray());
}
else if (clipboard[te])
{
te_data = clipboard[te];
}
else
{
return;
}
LLTextureEntry* tep = objectp->getTE(te);
if (tep)
{
if (te_data.has("te"))
{
// Color / Alpha
if (te_data["te"].has("colors"))
{
LLColor4 color = tep->getColor();
LLColor4 clip_color;
clip_color.setValue(te_data["te"]["colors"]);
// Color
color.mV[VRED] = clip_color.mV[VRED];
color.mV[VGREEN] = clip_color.mV[VGREEN];
color.mV[VBLUE] = clip_color.mV[VBLUE];
// Alpha
color.mV[VALPHA] = clip_color.mV[VALPHA];
objectp->setTEColor(te, color);
}
// Color/fullbright
if (te_data["te"].has("fullbright"))
{
objectp->setTEFullbright(te, te_data["te"]["fullbright"].asInteger());
}
// Glow
if (te_data["te"].has("glow"))
{
objectp->setTEGlow(te, (F32)te_data["te"]["glow"].asReal());
}
}
}
}
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();
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
if (!objectp || !node
|| objectp->getPCode() != LL_PCODE_VOLUME
|| !objectp->permModify()
|| objectp->isPermanentEnforced()
|| selected_count > 1
|| !LLMaterialEditor::canClipboardObjectsMaterial())
{
return;
}
if (mClipboardParams.has("texture"))
{
mClipboardParams["texture"].clear();
}
else
{
mClipboardParams["texture"] = LLSD::emptyArray();
}
std::map<LLUUID, LLUUID> asset_item_map;
// a way to resolve situations where source and target have different amount of faces
S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
mClipboardParams["texture_all_tes"] = (num_tes != 1) || (LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool());
for (S32 te = 0; te < num_tes; ++te)
{
if (node->isTESelected(te))
{
LLTextureEntry* tep = objectp->getTE(te);
if (tep)
{
LLSD te_data;
LLUUID pbr_id = objectp->getRenderMaterialID(te);
// asLLSD() includes media
te_data["te"] = tep->asLLSD();
te_data["te"]["shiny"] = tep->getShiny();
te_data["te"]["bumpmap"] = tep->getBumpmap();
te_data["te"]["bumpshiny"] = tep->getBumpShiny();
te_data["te"]["bumpfullbright"] = tep->getBumpShinyFullbright();
te_data["te"]["texgen"] = tep->getTexGen();
te_data["te"]["pbr"] = pbr_id;
if (tep->getGLTFMaterialOverride() != nullptr)
{
te_data["te"]["pbr_override"] = tep->getGLTFMaterialOverride()->asJSON();
}
if (te_data["te"].has("imageid") || pbr_id.notNull())
{
LLUUID img_id = te_data["te"]["imageid"].asUUID();
bool pbr_from_library = false;
bool pbr_full_perm = false;
bool is_creator = false;
if (objectp->permCopy()
&& objectp->permTransfer()
&& objectp->permModify())
{
// If agent created this object and nothing is limiting permissions, mark as full perm
// If agent was granted permission to edit objects owned and created by somebody else, mark full perm
// This check is not perfect since we can't figure out whom textures belong to so this ended up restrictive
std::string creator_app_link;
LLUUID creator_id;
LLSelectMgr::getInstance()->selectGetCreator(creator_id, creator_app_link);
is_creator = objectp->mOwnerID == creator_id;
}
// check permissions for blin-phong/diffuse image and for pbr asset
if (img_id.notNull())
{
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)
{
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLUUID& texture_id = override->mTextureId[i];
if (texture_id.notNull())
{
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);
}
}
}
}
}
LLMaterialPtr material_ptr = tep->getMaterialParams();
if (!material_ptr.isNull())
{
LLSD mat_data;
mat_data["NormMap"] = material_ptr->getNormalID();
mat_data["SpecMap"] = material_ptr->getSpecularID();
mat_data["NormRepX"] = material_ptr->getNormalRepeatX();
mat_data["NormRepY"] = material_ptr->getNormalRepeatY();
mat_data["NormOffX"] = material_ptr->getNormalOffsetX();
mat_data["NormOffY"] = material_ptr->getNormalOffsetY();
mat_data["NormRot"] = material_ptr->getNormalRotation();
mat_data["SpecRepX"] = material_ptr->getSpecularRepeatX();
mat_data["SpecRepY"] = material_ptr->getSpecularRepeatY();
mat_data["SpecOffX"] = material_ptr->getSpecularOffsetX();
mat_data["SpecOffY"] = material_ptr->getSpecularOffsetY();
mat_data["SpecRot"] = material_ptr->getSpecularRotation();
mat_data["SpecColor"] = material_ptr->getSpecularLightColor().getValue();
mat_data["SpecExp"] = material_ptr->getSpecularLightExponent();
mat_data["EnvIntensity"] = material_ptr->getEnvironmentIntensity();
mat_data["AlphaMaskCutoff"] = material_ptr->getAlphaMaskCutoff();
mat_data["DiffuseAlphaMode"] = material_ptr->getDiffuseAlphaMode();
// Replace no-copy textures, destination texture will get used instead if available
if (mat_data.has("NormMap"))
{
LLUUID id = mat_data["NormMap"].asUUID();
if (id.notNull() && !get_can_copy_texture(id))
{
mat_data["NormMap"] = DEFAULT_OBJECT_TEXTURE;
mat_data["NormMapNoCopy"] = true;
}
}
if (mat_data.has("SpecMap"))
{
LLUUID id = mat_data["SpecMap"].asUUID();
if (id.notNull() && !get_can_copy_texture(id))
{
mat_data["SpecMap"] = DEFAULT_OBJECT_TEXTURE;
mat_data["SpecMapNoCopy"] = true;
}
}
te_data["material"] = mat_data;
}
mClipboardParams["texture"].append(te_data);
}
}
}
}
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"))
{
return;
}
LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
if (!objectp || !node
|| objectp->getPCode() != LL_PCODE_VOLUME
|| !objectp->permModify()
|| objectp->isPermanentEnforced()
|| selected_count > 1
|| !LLMaterialEditor::canClipboardObjectsMaterial())
{
// not supposed to happen
LL_WARNS() << "Failed to paste texture due to missing or wrong selection" << LL_ENDL;
return;
}
bool face_selection_mode = LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool();
LLSD &clipboard = mClipboardParams["texture"]; // array
S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
S32 compare_tes = num_tes;
if (face_selection_mode)
{
compare_tes = 0;
for (S32 te = 0; te < num_tes; ++te)
{
if (node->isTESelected(te))
{
compare_tes++;
}
}
}
// we can copy if single face was copied in edit face mode or if face count matches
if (!((clipboard.size() == 1) && mClipboardParams["texture_all_tes"].asBoolean())
&& compare_tes != clipboard.size())
{
LLSD notif_args;
if (face_selection_mode)
{
static std::string reason = getString("paste_error_face_selection_mismatch");
notif_args["REASON"] = reason;
}
else
{
static std::string reason = getString("paste_error_object_face_count_mismatch");
notif_args["REASON"] = reason;
}
LLNotificationsUtil::add("FacePasteFailed", notif_args);
return;
}
bool full_perm_object = true;
LLSD::array_const_iterator iter = clipboard.beginArray();
LLSD::array_const_iterator end = clipboard.endArray();
for (; iter != end; ++iter)
{
const LLSD& te_data = *iter;
if (te_data.has("te"))
{
if (te_data["te"].has("imageid"))
{
bool full_perm = get_full_permission(te_data["te"], "img");
full_perm_object &= full_perm;
if (!full_perm)
{
if (!validateInventoryItem(te_data["te"], "img"))
{
return;
}
}
}
if (te_data["te"].has("pbr"))
{
bool full_perm = get_full_permission(te_data["te"], "pbr");
full_perm_object &= full_perm;
if (!full_perm)
{
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;
}
}
}
}
}
}
}
}
if (!full_perm_object)
{
LLNotificationsUtil::add("FacePasteTexturePermissions");
}
LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
LLPanelFacePasteTexFunctor paste_func(this, PASTE_TEXTURE);
selected_objects->applyToTEs(&paste_func);
LLPanelFaceUpdateFunctor sendfunc(true);
selected_objects->applyToObjects(&sendfunc);
LLGLTFMaterialList::flushUpdates();
LLPanelFaceNavigateHomeFunctor navigate_home_func;
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;
LLSD &clipboard = mClipboardParams["texture"]; // array
if ((clipboard.size() == 1) && mClipboardParams["texture_all_tes"].asBoolean())
{
te_data = *(clipboard.beginArray());
}
else if (clipboard[te])
{
te_data = clipboard[te];
}
else
{
return;
}
LLTextureEntry* tep = objectp->getTE(te);
if (tep)
{
if (te_data.has("te"))
{
// Texture
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* img_itemp_res = NULL;
get_item_and_permissions(imageid, img_itemp_res, img_full_perm, img_from_library, te_data["te"], "img");
if (img_itemp_res)
{
if (te == -1) // all faces
{
LLToolDragAndDrop::dropTextureAllFaces(objectp,
img_itemp_res,
img_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
LLUUID::null,
false);
}
else // one face
{
LLToolDragAndDrop::dropTextureOneFace(objectp,
te,
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 (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);
objectp->setTEImage(U8(te), image);
}
}
if (te_data["te"].has("bumpmap"))
{
objectp->setTEBumpmap(te, (U8)te_data["te"]["bumpmap"].asInteger());
}
if (te_data["te"].has("bumpshiny"))
{
objectp->setTEBumpShiny(te, (U8)te_data["te"]["bumpshiny"].asInteger());
}
if (te_data["te"].has("bumpfullbright"))
{
objectp->setTEBumpShinyFullbright(te, (U8)te_data["te"]["bumpfullbright"].asInteger());
}
if (te_data["te"].has("texgen"))
{
objectp->setTETexGen(te, (U8)te_data["te"]["texgen"].asInteger());
}
// PBR/GLTF
if (te_data["te"].has("pbr"))
{
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"))
{
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;
}
}
}
if (allow && pbr_itemp_res)
{
if (pbr_itemp_res)
{
allow = LLToolDragAndDrop::handleDropMaterialProtections(
objectp,
pbr_itemp_res,
pbr_from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT,
pbr_id);
}
else
{
allow = pbr_full_perm;
}
}
if (allow)
{
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
{
objectp->setRenderMaterialID(te, LLUUID::null, false /*send in bulk later*/ );
tep->setGLTFRenderMaterial(nullptr);
tep->setGLTFMaterialOverride(nullptr);
// blank out most override data on the server
LLGLTFMaterialList::queueApply(objectp, te, LLUUID::null);
}
// Texture map
if (te_data["te"].has("scales") && te_data["te"].has("scalet"))
{
objectp->setTEScale(te, (F32)te_data["te"]["scales"].asReal(), (F32)te_data["te"]["scalet"].asReal());
}
if (te_data["te"].has("offsets") && te_data["te"].has("offsett"))
{
objectp->setTEOffset(te, (F32)te_data["te"]["offsets"].asReal(), (F32)te_data["te"]["offsett"].asReal());
}
if (te_data["te"].has("imagerot"))
{
objectp->setTERotation(te, (F32)te_data["te"]["imagerot"].asReal());
}
// Media
if (te_data["te"].has("media_flags"))
{
U8 media_flags = te_data["te"]["media_flags"].asInteger();
objectp->setTEMediaFlags(te, media_flags);
LLVOVolume *vo = dynamic_cast<LLVOVolume*>(objectp);
if (vo && te_data["te"].has(LLTextureEntry::TEXTURE_MEDIA_DATA_KEY))
{
vo->syncMediaData(te, te_data["te"][LLTextureEntry::TEXTURE_MEDIA_DATA_KEY], true/*merge*/, true/*ignore_agent*/);
}
}
else
{
// Keep media flags on destination unchanged
}
}
if (te_data.has("material"))
{
LLUUID object_id = objectp->getID();
// Normal
// Replace placeholders with target's
if (te_data["material"].has("NormMapNoCopy"))
{
LLMaterialPtr material = tep->getMaterialParams();
if (material.notNull())
{
LLUUID id = material->getNormalID();
if (id.notNull())
{
te_data["material"]["NormMap"] = id;
}
}
}
LLSelectedTEMaterial::setNormalID(this, te_data["material"]["NormMap"].asUUID(), te, object_id);
LLSelectedTEMaterial::setNormalRepeatX(this, (F32)te_data["material"]["NormRepX"].asReal(), te, object_id);
LLSelectedTEMaterial::setNormalRepeatY(this, (F32)te_data["material"]["NormRepY"].asReal(), te, object_id);
LLSelectedTEMaterial::setNormalOffsetX(this, (F32)te_data["material"]["NormOffX"].asReal(), te, object_id);
LLSelectedTEMaterial::setNormalOffsetY(this, (F32)te_data["material"]["NormOffY"].asReal(), te, object_id);
LLSelectedTEMaterial::setNormalRotation(this, (F32)te_data["material"]["NormRot"].asReal(), te, object_id);
// Specular
// Replace placeholders with target's
if (te_data["material"].has("SpecMapNoCopy"))
{
LLMaterialPtr material = tep->getMaterialParams();
if (material.notNull())
{
LLUUID id = material->getSpecularID();
if (id.notNull())
{
te_data["material"]["SpecMap"] = id;
}
}
}
LLSelectedTEMaterial::setSpecularID(this, te_data["material"]["SpecMap"].asUUID(), te, object_id);
LLSelectedTEMaterial::setSpecularRepeatX(this, (F32)te_data["material"]["SpecRepX"].asReal(), te, object_id);
LLSelectedTEMaterial::setSpecularRepeatY(this, (F32)te_data["material"]["SpecRepY"].asReal(), te, object_id);
LLSelectedTEMaterial::setSpecularOffsetX(this, (F32)te_data["material"]["SpecOffX"].asReal(), te, object_id);
LLSelectedTEMaterial::setSpecularOffsetY(this, (F32)te_data["material"]["SpecOffY"].asReal(), te, object_id);
LLSelectedTEMaterial::setSpecularRotation(this, (F32)te_data["material"]["SpecRot"].asReal(), te, object_id);
LLColor4U spec_color(te_data["material"]["SpecColor"]);
LLSelectedTEMaterial::setSpecularLightColor(this, spec_color, te);
LLSelectedTEMaterial::setSpecularLightExponent(this, (U8)te_data["material"]["SpecExp"].asInteger(), te, object_id);
LLSelectedTEMaterial::setEnvironmentIntensity(this, (U8)te_data["material"]["EnvIntensity"].asInteger(), te, object_id);
LLSelectedTEMaterial::setDiffuseAlphaMode(this, (U8)te_data["material"]["DiffuseAlphaMode"].asInteger(), te, object_id);
LLSelectedTEMaterial::setAlphaMaskCutoff(this, (U8)te_data["material"]["AlphaMaskCutoff"].asInteger(), te, object_id);
if (te_data.has("te") && te_data["te"].has("shiny"))
{
objectp->setTEShiny(te, (U8)te_data["te"]["shiny"].asInteger());
}
}
}
}
// <FS> Extended copy & paste buttons
//void LLPanelFace::menuDoToSelected(const LLSD& userdata)
//{
// std::string command = userdata.asString();
//
// // paste
// if (command == "color_paste")
// {
// onPasteColor();
// }
// else if (command == "texture_paste")
// {
// onPasteTexture();
// }
// // copy
// else if (command == "color_copy")
// {
// onCopyColor();
// }
// else if (command == "texture_copy")
// {
// onCopyTexture();
// }
//}
//
//bool LLPanelFace::menuEnableItem(const LLSD& userdata)
//{
// std::string command = userdata.asString();
//
// // paste options
// if (command == "color_paste")
// {
// return mClipboardParams.has("color");
// }
// else if (command == "texture_paste")
// {
// return mClipboardParams.has("texture");
// }
// return false;
//}
void LLPanelFace::onCopyFaces()
{
onCopyTexture();
onCopyColor();
mBtnPasteFaces->setEnabled(!mClipboardParams.emptyMap() && (mClipboardParams.has("color") || mClipboardParams.has("texture")));
}
void LLPanelFace::onPasteFaces()
{
LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
LLPanelFacePasteTexFunctor paste_texture_func(this, PASTE_TEXTURE);
selected_objects->applyToTEs(&paste_texture_func);
LLPanelFacePasteTexFunctor paste_color_func(this, PASTE_COLOR);
selected_objects->applyToTEs(&paste_color_func);
LLPanelFaceUpdateFunctor sendfunc(true);
selected_objects->applyToObjects(&sendfunc);
LLPanelFaceNavigateHomeFunctor navigate_home_func;
selected_objects->applyToTEs(&navigate_home_func);
}
// </FS>
void LLPanelFace::onCommitPlanarAlign()
{
getState();
sendTextureInfo();
}
void LLPanelFace::updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::TextureTransform*)> edit)
{
const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo();
if (texture_info == LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT)
{
updateSelectedGLTFMaterials([&](LLGLTFMaterial* new_override)
{
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[(LLGLTFMaterial::TextureInfo)i];
edit(&new_transform);
}
});
}
else
{
updateSelectedGLTFMaterials([&](LLGLTFMaterial* new_override)
{
LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[texture_info];
edit(&new_transform);
});
}
}
void LLPanelFace::setMaterialOverridesFromSelection()
{
const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo();
U32 texture_info_start;
U32 texture_info_end;
if (texture_info == LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT)
{
texture_info_start = 0;
texture_info_end = LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT;
}
else
{
texture_info_start = texture_info;
texture_info_end = texture_info + 1;
}
bool read_transform = true;
LLGLTFMaterial::TextureTransform transform;
bool scale_u_same = true;
bool scale_v_same = true;
bool rotation_same = true;
bool offset_u_same = true;
bool offset_v_same = true;
for (U32 i = texture_info_start; i < texture_info_end; ++i)
{
LLGLTFMaterial::TextureTransform this_transform;
bool this_scale_u_same = true;
bool this_scale_v_same = true;
bool this_rotation_same = true;
bool this_offset_u_same = true;
bool this_offset_v_same = true;
readSelectedGLTFMaterial<float>([&](const LLGLTFMaterial* mat)
{
return mat ? mat->mTextureTransform[i].mScale[VX] : 0.f;
}, this_transform.mScale[VX], this_scale_u_same, true, 1e-3f);
readSelectedGLTFMaterial<float>([&](const LLGLTFMaterial* mat)
{
return mat ? mat->mTextureTransform[i].mScale[VY] : 0.f;
}, this_transform.mScale[VY], this_scale_v_same, true, 1e-3f);
readSelectedGLTFMaterial<float>([&](const LLGLTFMaterial* mat)
{
return mat ? mat->mTextureTransform[i].mRotation : 0.f;
}, this_transform.mRotation, this_rotation_same, true, 1e-3f);
readSelectedGLTFMaterial<float>([&](const LLGLTFMaterial* mat)
{
return mat ? mat->mTextureTransform[i].mOffset[VX] : 0.f;
}, this_transform.mOffset[VX], this_offset_u_same, true, 1e-3f);
readSelectedGLTFMaterial<float>([&](const LLGLTFMaterial* mat)
{
return mat ? mat->mTextureTransform[i].mOffset[VY] : 0.f;
}, this_transform.mOffset[VY], this_offset_v_same, true, 1e-3f);
scale_u_same = scale_u_same && this_scale_u_same;
scale_v_same = scale_v_same && this_scale_v_same;
rotation_same = rotation_same && this_rotation_same;
offset_u_same = offset_u_same && this_offset_u_same;
offset_v_same = offset_v_same && this_offset_v_same;
if (read_transform)
{
read_transform = false;
transform = this_transform;
}
else
{
scale_u_same = scale_u_same && (this_transform.mScale[VX] == transform.mScale[VX]);
scale_v_same = scale_v_same && (this_transform.mScale[VY] == transform.mScale[VY]);
rotation_same = rotation_same && (this_transform.mRotation == transform.mRotation);
offset_u_same = offset_u_same && (this_transform.mOffset[VX] == transform.mOffset[VX]);
offset_v_same = offset_v_same && (this_transform.mOffset[VY] == transform.mOffset[VY]);
}
}
mPBRScaleU->setValue(transform.mScale[VX]);
mPBRScaleV->setValue(transform.mScale[VY]);
mPBRRotate->setValue(transform.mRotation * RAD_TO_DEG);
mPBROffsetU->setValue(transform.mOffset[VX]);
mPBROffsetV->setValue(transform.mOffset[VY]);
mPBRScaleU->setTentative(!scale_u_same);
mPBRScaleV->setTentative(!scale_v_same);
mPBRRotate->setTentative(!rotation_same);
mPBROffsetU->setTentative(!offset_u_same);
mPBROffsetV->setTentative(!offset_v_same);
}
void LLPanelFace::Selection::connect()
{
if (!mSelectConnection.connected())
{
mSelectConnection = LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLPanelFace::Selection::onSelectionChanged, this));
}
}
bool LLPanelFace::Selection::update()
{
const bool changed = mChanged || compareSelection();
mChanged = false;
return changed;
}
void LLPanelFace::Selection::onSelectedObjectUpdated(const LLUUID& object_id, S32 side)
{
if (object_id == mSelectedObjectID)
{
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;
}
}
}
}
bool LLPanelFace::Selection::compareSelection()
{
if (!mNeedsSelectionCheck)
{
return false;
}
mNeedsSelectionCheck = false;
const S32 old_object_count = mSelectedObjectCount;
const S32 old_te_count = mSelectedTECount;
const LLUUID old_object_id = mSelectedObjectID;
const S32 old_side = mLastSelectedSide;
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
LLSelectNode* node = selection->getFirstNode();
if (node)
{
LLViewerObject* object = node->getObject();
mSelectedObjectCount = selection->getObjectCount();
mSelectedTECount = selection->getTECount();
mSelectedObjectID = object->getID();
mLastSelectedSide = node->getLastSelectedTE();
}
else
{
mSelectedObjectCount = 0;
mSelectedTECount = 0;
mSelectedObjectID = LLUUID::null;
mLastSelectedSide = -1;
}
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;
}
void LLPanelFace::onCommitGLTFTextureScaleU()
{
F32 value = (F32)mPBRScaleU->getValue().asReal();
updateGLTFTextureTransform([&](LLGLTFMaterial::TextureTransform* new_transform)
{
new_transform->mScale.mV[VX] = value;
});
}
void LLPanelFace::onCommitGLTFTextureScaleV()
{
F32 value = (F32)mPBRScaleV->getValue().asReal();
updateGLTFTextureTransform([&](LLGLTFMaterial::TextureTransform* new_transform)
{
new_transform->mScale.mV[VY] = value;
});
}
void LLPanelFace::onCommitGLTFRotation()
{
F32 value = (F32)mPBRRotate->getValue().asReal() * DEG_TO_RAD;
updateGLTFTextureTransform([&](LLGLTFMaterial::TextureTransform* new_transform)
{
new_transform->mRotation = value;
});
}
void LLPanelFace::onCommitGLTFTextureOffsetU()
{
F32 value = (F32)mPBROffsetU->getValue().asReal();
updateGLTFTextureTransform([&](LLGLTFMaterial::TextureTransform* new_transform)
{
new_transform->mOffset.mV[VX] = value;
});
}
void LLPanelFace::onCommitGLTFTextureOffsetV()
{
F32 value = (F32)mPBROffsetV->getValue().asReal();
updateGLTFTextureTransform([&](LLGLTFMaterial::TextureTransform* new_transform)
{
new_transform->mOffset.mV[VY] = value;
});
}
void LLPanelFace::onTextureSelectionChanged(LLInventoryItem* itemp)
{
LL_DEBUGS("Materials") << "item asset " << itemp->getAssetUUID() << LL_ENDL;
LLTextureCtrl* texture_ctrl;
U32 mattype = mRadioMaterialType->getSelectedIndex();
switch (mattype)
{
case MATTYPE_SPECULAR:
texture_ctrl = mShinyTextureCtrl;
break;
case MATTYPE_NORMAL:
texture_ctrl = mBumpyTextureCtrl;
break;
default:
texture_ctrl = mTextureCtrl;
}
LLUUID obj_owner_id;
std::string obj_owner_name;
LLSelectMgr::instance().selectGetOwner(obj_owner_id, obj_owner_name);
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?
if (can_copy && can_transfer)
{
texture_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
texture_ctrl->setCanApply(false, can_transfer ? true : is_object_owner && not_for_sale);
if (gSavedSettings.getBOOL("TextureLivePreview"))
{
LLNotificationsUtil::add("LivePreviewUnavailable");
}
}
void LLPanelFace::onPbrSelectionChanged(LLInventoryItem* itemp)
{
if (mPBRTextureCtrl)
{
LLUUID obj_owner_id;
std::string obj_owner_name;
LLSelectMgr::instance().selectGetOwner(obj_owner_id, obj_owner_name);
LLSaleInfo sale_info;
LLSelectMgr::instance().selectGetSaleInfo(sale_info);
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?
bool from_library = ALEXANDRIA_LINDEN_ID == itemp->getPermissions().getOwner();
if ((can_copy && can_transfer && can_modify) || from_library)
{
mPBRTextureCtrl->setCanApply(true, true);
return;
}
// if material has (no-transfer) attribute it can be applied only for object which we own and is not for sale
mPBRTextureCtrl->setCanApply(false, can_transfer ? true : is_object_owner && not_for_sale);
if (gSavedSettings.getBOOL("TextureLivePreview"))
{
LLNotificationsUtil::add("LivePreviewUnavailablePBR");
}
}
}
bool LLPanelFace::isIdenticalPlanarTexgen()
{
LLTextureEntry::e_texgen selected_texgen = LLTextureEntry::TEX_GEN_DEFAULT;
bool identical_texgen = false;
LLSelectedTE::getTexGen(selected_texgen, identical_texgen);
return (identical_texgen && (selected_texgen == LLTextureEntry::TEX_GEN_PLANAR));
}
bool LLPanelFace::isMediaTexSelected()
{
LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode();
if (LLViewerObject* objectp = node->getObject())
{
S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
for (S32 te = 0; te < num_tes; ++te)
{
if (node->isTESelected(te))
{
if (objectp->getTE(te) && objectp->getTE(te)->hasMedia())
{
return true;
}
}
}
}
return false;
}
void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical_face)
{
struct LLSelectedTEGetFace : public LLSelectedTEGetFunctor<LLFace *>
{
LLFace* get(LLViewerObject* object, S32 te)
{
return (object->mDrawable) ? object->mDrawable->getFace(te): NULL;
}
} get_te_face_func;
identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, face_to_return, false, (LLFace*)nullptr);
}
void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face, bool& missing_asset)
{
struct LLSelectedTEGetmatId : public LLSelectedTEFunctor
{
LLSelectedTEGetmatId()
: mImageFormat(GL_RGB)
, mIdentical(true)
, mMissingAsset(false)
, mFirstRun(true)
{
}
bool apply(LLViewerObject* object, S32 te_index) override
{
LLViewerTexture* image = object ? object->getTEImage(te_index) : nullptr;
LLGLenum format = GL_RGB;
bool missing = false;
if (image)
{
format = image->getPrimaryFormat();
missing = image->isMissingAsset();
}
if (mFirstRun)
{
mFirstRun = false;
mImageFormat = format;
mMissingAsset = missing;
}
else
{
mIdentical &= (mImageFormat == format);
mIdentical &= (mMissingAsset == missing);
}
return true;
}
LLGLenum mImageFormat;
bool mIdentical;
bool mMissingAsset;
bool mFirstRun;
} func;
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func);
image_format_to_return = func.mImageFormat;
identical_face = func.mIdentical;
missing_asset = func.mMissingAsset;
}
void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical)
{
struct LLSelectedTEGetTexId : public LLSelectedTEGetFunctor<LLUUID>
{
LLUUID get(LLViewerObject* object, S32 te_index)
{
LLTextureEntry *te = object->getTE(te_index);
if (te)
{
if ((te->getID() == IMG_USE_BAKED_EYES) || (te->getID() == IMG_USE_BAKED_HAIR) || (te->getID() == IMG_USE_BAKED_HEAD) || (te->getID() == IMG_USE_BAKED_LOWER) || (te->getID() == IMG_USE_BAKED_SKIRT) || (te->getID() == IMG_USE_BAKED_UPPER)
|| (te->getID() == IMG_USE_BAKED_LEFTARM) || (te->getID() == IMG_USE_BAKED_LEFTLEG) || (te->getID() == IMG_USE_BAKED_AUX1) || (te->getID() == IMG_USE_BAKED_AUX2) || (te->getID() == IMG_USE_BAKED_AUX3))
{
return te->getID();
}
}
LLUUID id;
LLViewerTexture* image = object->getTEImage(te_index);
if (image)
{
id = image->getID();
}
if (!id.isNull() && LLViewerMedia::getInstance()->textureHasMedia(id))
{
if (te)
{
LLViewerTexture* tex = te->getID().notNull() ? gTextureList.findImage(te->getID(), TEX_LIST_STANDARD) : NULL;
if(!tex)
{
tex = LLViewerFetchedTexture::sDefaultImagep;
}
if (tex)
{
id = tex->getID();
}
}
}
return id;
}
} func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, id );
}
void LLPanelFace::LLSelectedTE::getPbrMaterialId(LLUUID& id, bool& identical, bool& has_faces_with_pbr, bool& has_faces_without_pbr)
{
struct LLSelectedTEGetmatId : public LLSelectedTEFunctor
{
LLSelectedTEGetmatId()
: mHasFacesWithoutPBR(false)
, mHasFacesWithPBR(false)
, mIdenticalId(true)
, mIdenticalOverride(true)
, mInitialized(false)
, mMaterialOverride(LLGLTFMaterial::sDefault)
{
}
bool apply(LLViewerObject* object, S32 te_index) override
{
LLUUID pbr_id = object->getRenderMaterialID(te_index);
if (pbr_id.isNull())
{
mHasFacesWithoutPBR = true;
}
else
{
mHasFacesWithPBR = true;
}
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;
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;
}
void LLPanelFace::LLSelectedTEMaterial::getCurrent(LLMaterialPtr& material_ptr, bool& identical_material)
{
struct MaterialFunctor : public LLSelectedTEGetFunctor<LLMaterialPtr>
{
LLMaterialPtr get(LLViewerObject* object, S32 te_index)
{
return object->getTEref(te_index).getMaterialParams();
}
} func;
identical_material = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_ptr);
}
void LLPanelFace::LLSelectedTEMaterial::getMaxSpecularRepeats(F32& repeats, bool& identical)
{
struct LLSelectedTEGetMaxSpecRepeats : public LLSelectedTEGetFunctor<F32>
{
F32 get(LLViewerObject* object, S32 face)
{
LLMaterial* mat = object->getTEref(face).getMaterialParams().get();
U32 s_axis = VX;
U32 t_axis = VY;
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
F32 repeats_s = 1.0f;
F32 repeats_t = 1.0f;
if (mat)
{
mat->getSpecularRepeat(repeats_s, repeats_t);
repeats_s /= object->getScale().mV[s_axis];
repeats_t /= object->getScale().mV[t_axis];
}
return llmax(repeats_s, repeats_t);
}
} max_spec_repeats_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_spec_repeats_func, repeats);
}
void LLPanelFace::LLSelectedTEMaterial::getMaxNormalRepeats(F32& repeats, bool& identical)
{
struct LLSelectedTEGetMaxNormRepeats : public LLSelectedTEGetFunctor<F32>
{
F32 get(LLViewerObject* object, S32 face)
{
LLMaterial* mat = object->getTEref(face).getMaterialParams().get();
U32 s_axis = VX;
U32 t_axis = VY;
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
F32 repeats_s = 1.0f;
F32 repeats_t = 1.0f;
if (mat)
{
mat->getNormalRepeat(repeats_s, repeats_t);
repeats_s /= object->getScale().mV[s_axis];
repeats_t /= object->getScale().mV[t_axis];
}
return llmax(repeats_s, repeats_t);
}
} max_norm_repeats_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_norm_repeats_func, repeats);
}
void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha)
{
struct LLSelectedTEGetDiffuseAlphaMode : public LLSelectedTEGetFunctor<U8>
{
LLSelectedTEGetDiffuseAlphaMode() : _isAlpha(false) {}
LLSelectedTEGetDiffuseAlphaMode(bool diffuse_texture_has_alpha) : _isAlpha(diffuse_texture_has_alpha) {}
virtual ~LLSelectedTEGetDiffuseAlphaMode() {}
U8 get(LLViewerObject* object, S32 face)
{
U8 diffuse_mode = _isAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE;
LLTextureEntry* tep = object->getTE(face);
if (tep)
{
LLMaterial* mat = tep->getMaterialParams().get();
if (mat)
{
diffuse_mode = mat->getDiffuseAlphaMode();
}
}
return diffuse_mode;
}
bool _isAlpha; // whether or not the diffuse texture selected contains alpha information
} get_diff_mode(diffuse_texture_has_alpha);
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode);
}
void LLPanelFace::LLSelectedTE::getObjectScaleS(F32& scale_s, bool& identical)
{
struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
{
F32 get(LLViewerObject* object, S32 face)
{
U32 s_axis = VX;
U32 t_axis = VY;
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
return object->getScale().mV[s_axis];
}
} scale_s_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &scale_s_func, scale_s );
}
void LLPanelFace::LLSelectedTE::getObjectScaleT(F32& scale_t, bool& identical)
{
struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
{
F32 get(LLViewerObject* object, S32 face)
{
U32 s_axis = VX;
U32 t_axis = VY;
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
return object->getScale().mV[t_axis];
}
} scale_t_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &scale_t_func, scale_t );
}
void LLPanelFace::LLSelectedTE::getMaxDiffuseRepeats(F32& repeats, bool& identical)
{
struct LLSelectedTEGetMaxDiffuseRepeats : public LLSelectedTEGetFunctor<F32>
{
F32 get(LLViewerObject* object, S32 face)
{
U32 s_axis = VX;
U32 t_axis = VY;
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
F32 repeats_s = object->getTEref(face).mScaleS / object->getScale().mV[s_axis];
F32 repeats_t = object->getTEref(face).mScaleT / object->getScale().mV[t_axis];
return llmax(repeats_s, repeats_t);
}
} max_diff_repeats_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_diff_repeats_func, repeats );
}
// <FS:CR> Materials alignment
void LLPanelFace::onClickMapsSync()
{
getState();
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
alignMaterialsProperties();
}
}
void LLPanelFace::alignMaterialsProperties()
{
// <FS:TS> FIRE-11911: Synchronize materials doesn't work with planar textures
// Don't even try to do the alignment if we wind up here and planar is enabled.
if (mPlanarAlign->getValue().asBoolean())
{
return;
}
// </FS:TS> FIRE-11911
F32 tex_scale_u = getCurrentTextureScaleU();
F32 tex_scale_v = getCurrentTextureScaleV();
F32 tex_offset_u = getCurrentTextureOffsetU();
F32 tex_offset_v = getCurrentTextureOffsetV();
F32 tex_rot = getCurrentTextureRot();
//<FS:TS> FIRE-12275: Material offset not working correctly
// Since the server cannot store negative offsets for materials
// textures, we normalize them to equivalent positive values here.
tex_offset_u = (tex_offset_u < 0.0f) ? 1.0f + tex_offset_u : tex_offset_u;
tex_offset_v = (tex_offset_v < 0.0f) ? 1.0f + tex_offset_v : tex_offset_v;
//</FS:TS> FIRE-12275
//<FS:TS> FIRE-12831: Negative rotations revert to zero
// The same goes for rotations as for offsets.
tex_rot = (tex_rot < 0.0f) ? 360.0f + tex_rot : tex_rot;
//</FS:TS> FIRE-12831
mShinyScaleU->setValue(tex_scale_u);
mShinyScaleV->setValue(tex_scale_v);
mShinyOffsetU->setValue(tex_offset_u);
mShinyOffsetV->setValue(tex_offset_v);
mShinyRotate->setValue(tex_rot);
LLSelectedTEMaterial::setSpecularRepeatX(this, tex_scale_u);
LLSelectedTEMaterial::setSpecularRepeatY(this, tex_scale_v);
LLSelectedTEMaterial::setSpecularOffsetX(this, tex_offset_u);
LLSelectedTEMaterial::setSpecularOffsetY(this, tex_offset_v);
LLSelectedTEMaterial::setSpecularRotation(this, tex_rot * DEG_TO_RAD);
mBumpyScaleU->setValue(tex_scale_u);
mBumpyScaleV->setValue(tex_scale_v);
mBumpyOffsetU->setValue(tex_offset_u);
mBumpyOffsetV->setValue(tex_offset_v);
mBumpyRotate->setValue(tex_rot);
LLSelectedTEMaterial::setNormalRepeatX(this, tex_scale_u);
LLSelectedTEMaterial::setNormalRepeatY(this, tex_scale_v);
LLSelectedTEMaterial::setNormalOffsetX(this, tex_offset_u);
LLSelectedTEMaterial::setNormalOffsetY(this, tex_offset_v);
LLSelectedTEMaterial::setNormalRotation(this, tex_rot * DEG_TO_RAD);
}
// <FS:CR> FIRE-11407 - Flip buttons
void LLPanelFace::onCommitFlip(const LLSD& user_data)
{
std::string user_data_string = user_data.asString();
if (user_data_string.empty())
return;
std::string control_name = "";
U32 mattype = mRadioMaterialType->getSelectedIndex();
switch (mattype)
{
case MATTYPE_DIFFUSE:
control_name = "TexScale" + user_data_string;
break;
case MATTYPE_NORMAL:
control_name = "bumpyScale" + user_data_string;
break;
case MATTYPE_SPECULAR:
control_name = "shinyScale" + user_data_string;
break;
default:
llassert(mattype);
return;
}
LLUICtrl* spinner = findChild<LLUICtrl>(control_name);
if (spinner)
{
F32 value = -(F32)(spinner->getValue().asReal());
spinner->setValue(value);
switch (mRadioMaterialType->getSelectedIndex())
{
case MATTYPE_DIFFUSE:
sendTextureInfo();
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
alignMaterialsProperties();
}
break;
case MATTYPE_NORMAL:
if (user_data_string == "U")
LLSelectedTEMaterial::setNormalRepeatX(this, value);
else if (user_data_string == "V")
LLSelectedTEMaterial::setNormalRepeatY(this, value);
break;
case MATTYPE_SPECULAR:
if (user_data_string == "U")
LLSelectedTEMaterial::setSpecularRepeatX(this, value);
else if (user_data_string == "V")
LLSelectedTEMaterial::setSpecularRepeatY(this, value);
break;
default:
llassert(mattype);
return;
}
}
}
void LLPanelFace::changePrecision(S32 decimal_precision)
{
mTexScaleU->setPrecision(decimal_precision);
mTexScaleV->setPrecision(decimal_precision);
mBumpyScaleU->setPrecision(decimal_precision);
mBumpyScaleV->setPrecision(decimal_precision);
mShinyScaleU->setPrecision(decimal_precision);
mShinyScaleV->setPrecision(decimal_precision);
mTexOffsetU->setPrecision(decimal_precision);
mTexOffsetV->setPrecision(decimal_precision);
mBumpyOffsetU->setPrecision(decimal_precision);
mBumpyOffsetV->setPrecision(decimal_precision);
mShinyOffsetU->setPrecision(decimal_precision);
mShinyOffsetV->setPrecision(decimal_precision);
mTexRotate->setPrecision(decimal_precision);
mBumpyRotate->setPrecision(decimal_precision);
mShinyRotate->setPrecision(decimal_precision);
mTexRepeat->setPrecision(decimal_precision);
}
// </FS:CR>
// <FS:Zi> Find all faces with same texture
void LLPanelFace::onClickBtnSelectSameTexture(const LLSD& user_data)
{
char channel = user_data.asStringRef()[0];
std::unordered_set<LLViewerObject*> objects;
// get a list of all linksets where at least one face is selected
for (auto iter = LLSelectMgr::getInstance()->getSelection()->valid_begin();
iter != LLSelectMgr::getInstance()->getSelection()->valid_end(); iter++)
{
objects.insert((*iter)->getObject()->getRootEdit());
}
// clean out the selection
LLSelectMgr::getInstance()->deselectAll();
// select all faces of all linksets that were found before
LLObjectSelectionHandle handle;
for (auto objectp : objects)
{
handle = LLSelectMgr::getInstance()->selectObjectAndFamily(objectp, true, false);
}
// grab the texture ID from the texture selector
LLTextureCtrl* texture_control = mTextureCtrl;
if (channel == 'n')
{
texture_control = mBumpyTextureCtrl;
}
else if (channel == 's')
{
texture_control = mShinyTextureCtrl;
}
LLUUID id = texture_control->getImageAssetID();
// go through all selected links in all selecrted linksets
for (auto iter = handle->begin(); iter != handle->end(); iter++)
{
LLSelectNode* node = *iter;
LLViewerObject* objectp = node->getObject();
U8 te_count = objectp->getNumTEs();
for (U8 i = 0; i < te_count; i++)
{
LLUUID image_id;
if (channel == 'd')
{
image_id = objectp->getTEImage(i)->getID();
}
else
{
const LLMaterialPtr mat = objectp->getTEref(i).getMaterialParams();
if (mat.notNull())
{
if (channel == 'n')
{
image_id = mat->getNormalID();
}
else if (channel == 's')
{
image_id = mat->getSpecularID();
}
}
}
// deselect all faces that use a different texture UUID
if (image_id != id)
{
objectp->setTESelected(i, false);
node->selectTE(i, false);
}
}
}
}
// </FS:Zi>