/** * @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" 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(); } // // 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(); }); getChildSetClickedCallback(mBtnEditBbr, "edit_selected_pbr", [&](LLUICtrl*, const LLSD&) { onClickBtnEditPBR(); }); getChildSetClickedCallback(mBtnSaveBbr, "save_selected_pbr", [&](LLUICtrl*, const LLSD&) { onClickBtnSavePBR(); }); 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(); }); 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(); }); mMenuClipboardColor = getChild<LLMenuButton>("clipboard_color_params_btn"); mMenuClipboardTexture = getChild<LLMenuButton>("clipboard_texture_params_btn"); 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"); clearCtrls(); return true; } LLPanelFace::LLPanelFace() : LLPanel(), mIsAlpha(false), mComboMatMedia(NULL), mTitleMedia(NULL), mTitleMediaText(NULL), mNeedMediaTitle(true) { USE_TEXTURE = LLTrans::getString("use_texture"); mCommitCallbackRegistrar.add("PanelFace.menuDoToSelected", boost::bind(&LLPanelFace::menuDoToSelected, this, _2)); mEnableCallbackRegistrar.add("PanelFace.menuEnable", boost::bind(&LLPanelFace::menuEnableItem, this, _2)); } 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(); 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); 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); 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); // Color swatch mLabelColor->setEnabled(editable); LLColor4 color = LLColor4::white; bool identical_color = false; LLSelectedTE::getColor(color, identical_color); 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); F32 transparency = (1.f - color.mV[VALPHA]) * 100.f; 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 { LLGLenum image_format = GL_RGB; bool identical_image_format = false; bool missing_asset = false; LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset); if (!missing_asset) { mIsAlpha = false; switch (image_format) { case GL_RGBA: case GL_ALPHA: { mIsAlpha = true; } break; case GL_RGB: break; default: { LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; } break; } } else { // Don't know image's properties, use material's mode value mIsAlpha = true; } if (LLViewerMedia::getInstance()->textureHasMedia(id)) { mBtnAlign->setEnabled(editable); } // Diffuse Alpha Mode // Init to the default that is appropriate for the alpha content of the asset // U8 alpha_mode = mIsAlpha ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; bool identical_alpha_mode = false; // See if that's been overridden by a material setting for same... // LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(alpha_mode, identical_alpha_mode, mIsAlpha); // it is invalid to have any alpha mode other than blend if transparency is greater than zero ... // Want masking? Want emissive? Tough! You get BLEND! alpha_mode = (transparency > 0.f) ? LLMaterial::DIFFUSE_ALPHA_MODE_BLEND : alpha_mode; // ... unless there is no alpha channel in the texture, in which case alpha mode MUST be none alpha_mode = mIsAlpha ? alpha_mode : LLMaterial::DIFFUSE_ALPHA_MODE_NONE; mComboAlphaMode->getSelectionInterface()->selectNthItem(alpha_mode); updateAlphaControls(); if (mTextureCtrl) { if (identical_diffuse) { 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); mShinyScaleU->setEnabled(editable && has_material && specmap_id.notNull()); mBumpyScaleU->setEnabled(editable && has_material && normmap_id.notNull()); 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)); } { 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); mShinyScaleV->setEnabled(editable && has_material && specmap_id.notNull()); mBumpyScaleV->setEnabled(editable && has_material && normmap_id.notNull()); 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); mShinyOffsetU->setEnabled(editable && has_material && specmap_id.notNull()); mBumpyOffsetU->setEnabled(editable && has_material && normmap_id.notNull()); } { 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); mShinyOffsetV->setEnabled(editable && has_material && specmap_id.notNull()); mBumpyOffsetV->setEnabled(editable && has_material && normmap_id.notNull()); } // 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); mShinyRotate->setEnabled(editable && has_material && specmap_id.notNull()); mBumpyRotate->setEnabled(editable && has_material && normmap_id.notNull()); 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())); } 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())); } 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); } } // 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); mMenuClipboardColor->setEnabled(editable && single_volume); // 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()); } 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); mBtnEditBbr->setEnabled(false); mBtnSaveBbr->setEnabled(false); 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); 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); 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(); mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify() && !objectp->isPermanentEnforced() && !objectp->isInventoryPending() && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1) && LLMaterialEditor::canClipboardObjectsMaterial()); std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options"); mMenuClipboardTexture->setToolTip(tooltip); } 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->getTE(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); // 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); } 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::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::getObjectScaleS(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->getTE(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 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; // 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"] = objectp->getRenderMaterialID(te); if (tep->getGLTFMaterialOverride() != nullptr) { te_data["te"]["pbr_override"] = tep->getGLTFMaterialOverride()->asJSON(); } if (te_data["te"].has("imageid")) { LLUUID item_id; LLUUID id = te_data["te"]["imageid"].asUUID(); bool from_library = get_is_predefined_texture(id); bool full_perm = from_library; if (!full_perm && 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); full_perm = objectp->mOwnerID == creator_id; } if (id.notNull() && !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; } { te_data["te"]["itemfullperm"] = full_perm; te_data["te"]["fromlibrary"] = from_library; // If full permission object, texture is free to copy, // but otherwise we need to check inventory and extract permissions // // Normally we care only about restrictions for current user and objects // don't inherit any 'next owner' permissions from texture, so there is // no need to record item id if full_perm==true if (!full_perm && !from_library && item_id.notNull()) { LLViewerInventoryItem* itemp = gInventory.getItem(item_id); if (itemp) { LLPermissions item_permissions = itemp->getPermissions(); if (item_permissions.allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { te_data["te"]["imageitemid"] = item_id; te_data["te"]["itemfullperm"] = itemp->getIsFullPerm(); if (!itemp->isFinished()) { // needed for dropTextureAllFaces LLInventoryModelBackgroundFetch::instance().start(item_id, false); } } } } } } 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); } } } } 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") && te_data["te"].has("imageid")) { bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); full_perm_object &= full_perm; if (!full_perm) { if (te_data["te"].has("imageitemid")) { LLUUID item_id = te_data["te"]["imageitemid"].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; } } } 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; } } } } 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 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 bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); bool from_library = te_data["te"].has("fromlibrary") && te_data["te"]["fromlibrary"].asBoolean(); if (te_data["te"].has("imageid")) { const LLUUID& imageid = te_data["te"]["imageid"].asUUID(); //texture or asset id LLViewerInventoryItem* itemp_res = NULL; if (te_data["te"].has("imageitemid")) { LLUUID item_id = te_data["te"]["imageitemid"].asUUID(); if (item_id.notNull()) { LLViewerInventoryItem* itemp = gInventory.getItem(item_id); if (itemp && itemp->isFinished()) { // dropTextureAllFaces will fail if incomplete itemp_res = itemp; } else { // Theoretically shouldn't happend, but if it does happen, we // might need to add a notification to user that paste will fail // since inventory isn't fully loaded LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL; } } } // for case when item got removed from inventory after we pressed 'copy' // or texture got pasted into previous object if (!itemp_res && !full_perm) { // Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable. LL_INFOS() << "Item " << te_data["te"]["imageitemid"].asUUID() << " no longer in inventory." << LL_ENDL; // Todo: fix this, we are often searching same texture multiple times (equal to number of faces) // Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; LLAssetIDMatches asset_id_matches(imageid); gInventory.collectDescendentsIf(LLUUID::null, cats, items, LLInventoryModel::INCLUDE_TRASH, asset_id_matches); // Extremely unreliable and perfomance unfriendly. // But we need this to check permissions and it is how texture control finds items for (S32 i = 0; i < items.size(); i++) { LLViewerInventoryItem* itemp = items[i]; if (itemp && itemp->isFinished()) { // dropTextureAllFaces will fail if incomplete LLPermissions item_permissions = itemp->getPermissions(); if (item_permissions.allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { itemp_res = itemp; break; // first match } } } } if (itemp_res) { if (te == -1) // all faces { LLToolDragAndDrop::dropTextureAllFaces(objectp, itemp_res, from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null, false); } else // one face { LLToolDragAndDrop::dropTextureOneFace(objectp, te, itemp_res, from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null, false, 0); } } // not an inventory item or no complete items else if (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")) { objectp->setRenderMaterialID(te, te_data["te"]["pbr"].asUUID(), false /*managing our own update*/); tep->setGLTFRenderMaterial(nullptr); tep->setGLTFMaterialOverride(nullptr); LLSD override_data; override_data["object_id"] = objectp->getID(); override_data["side"] = te; if (te_data["te"].has("pbr_override")) { override_data["gltf_json"] = te_data["te"]["pbr_override"]; } else { override_data["gltf_json"] = ""; } override_data["asset_id"] = te_data["te"]["pbr"].asUUID(); LLGLTFMaterialList::queueUpdate(override_data); } 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()); } } } } 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::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)); } 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->getTE(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->getTE(face)->getMaterialParams().get(); U32 s_axis = VX; U32 t_axis = VY; 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->getTE(face)->getMaterialParams().get(); U32 s_axis = VX; U32 t_axis = VY; 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->getTE(face)->mScaleS / object->getScale().mV[s_axis]; F32 repeats_t = object->getTE(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 ); }