diff options
Diffstat (limited to 'indra/llappearance/lltexlayer.cpp')
-rw-r--r-- | indra/llappearance/lltexlayer.cpp | 3840 |
1 files changed, 1920 insertions, 1920 deletions
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index 15a031f90c..d1913134c4 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -1,1920 +1,1920 @@ -/** - * @file lltexlayer.cpp - * @brief A texture layer. Used for avatars. - * - * $LicenseInfo:firstyear=2002&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 "linden_common.h" - -#include "lltexlayer.h" - -#include "llavatarappearance.h" -#include "llcrc.h" -#include "llimagej2c.h" -#include "llimagetga.h" -#include "lldir.h" -#include "lltexlayerparams.h" -#include "lltexturemanagerbridge.h" -#include "lllocaltextureobject.h" -#include "../llui/llui.h" -#include "llwearable.h" -#include "llwearabledata.h" -#include "llvertexbuffer.h" -#include "llviewervisualparam.h" -#include "llfasttimer.h" - -//#include "../tools/imdebug/imdebug.h" - -using namespace LLAvatarAppearanceDefines; - -// runway consolidate -extern std::string self_av_string(); - -class LLTexLayerInfo -{ - friend class LLTexLayer; - friend class LLTexLayerTemplate; - friend class LLTexLayerInterface; -public: - LLTexLayerInfo(); - ~LLTexLayerInfo(); - - bool parseXml(LLXmlTreeNode* node); - bool createVisualParams(LLAvatarAppearance *appearance); - bool isUserSettable() { return mLocalTexture != -1; } - S32 getLocalTexture() const { return mLocalTexture; } - bool getOnlyAlpha() const { return mUseLocalTextureAlphaOnly; } - std::string getName() const { return mName; } - -private: - std::string mName; - - bool mWriteAllChannels; // Don't use masking. Just write RGBA into buffer, - LLTexLayerInterface::ERenderPass mRenderPass; - - std::string mGlobalColor; - LLColor4 mFixedColor; - - S32 mLocalTexture; - std::string mStaticImageFileName; - bool mStaticImageIsMask; - bool mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask - bool mIsVisibilityMask; - - typedef std::vector< std::pair< std::string,bool > > morph_name_list_t; - morph_name_list_t mMorphNameList; - param_color_info_list_t mParamColorInfoList; - param_alpha_info_list_t mParamAlphaInfoList; -}; - -//----------------------------------------------------------------------------- -// LLTexLayerSetBuffer -// The composite image that a LLViewerTexLayerSet writes to. Each LLViewerTexLayerSet has one. -//----------------------------------------------------------------------------- - -LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner) : - mTexLayerSet(owner) -{ -} - -LLTexLayerSetBuffer::~LLTexLayerSetBuffer() -{ -} - -void LLTexLayerSetBuffer::pushProjection() const -{ - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, getCompositeWidth(), 0.0f, getCompositeHeight(), -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); -} - -void LLTexLayerSetBuffer::popProjection() const -{ - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); -} - -// virtual -void LLTexLayerSetBuffer::preRenderTexLayerSet() -{ - // Set up an ortho projection - pushProjection(); -} - -// virtual -void LLTexLayerSetBuffer::postRenderTexLayerSet(bool success) -{ - popProjection(); -} - -bool LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target) -{ - // Default color mask for tex layer render - gGL.setColorMask(true, true); - - bool success = true; - - gAlphaMaskProgram.bind(); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - - LLVertexBuffer::unbind(); - - // Composite the color data - LLGLSUIDefault gls_ui; - success &= mTexLayerSet->render( getCompositeOriginX(), getCompositeOriginY(), - getCompositeWidth(), getCompositeHeight(), bound_target ); - gGL.flush(); - - midRenderTexLayerSet(success); - - gAlphaMaskProgram.unbind(); - - LLVertexBuffer::unbind(); - - // reset GL state - gGL.setColorMask(true, true); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - return success; -} - -//----------------------------------------------------------------------------- -// LLTexLayerSetInfo -// An ordered set of texture layers that get composited into a single texture. -//----------------------------------------------------------------------------- - -LLTexLayerSetInfo::LLTexLayerSetInfo() : - mBodyRegion( "" ), - mWidth( 512 ), - mHeight( 512 ), - mClearAlpha( true ) -{ -} - -LLTexLayerSetInfo::~LLTexLayerSetInfo( ) -{ - std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer()); - mLayerInfoList.clear(); -} - -bool LLTexLayerSetInfo::parseXml(LLXmlTreeNode* node) -{ - llassert( node->hasName( "layer_set" ) ); - if( !node->hasName( "layer_set" ) ) - { - return false; - } - - // body_region - static LLStdStringHandle body_region_string = LLXmlTree::addAttributeString("body_region"); - if( !node->getFastAttributeString( body_region_string, mBodyRegion ) ) - { - LL_WARNS() << "<layer_set> is missing body_region attribute" << LL_ENDL; - return false; - } - - // width, height - static LLStdStringHandle width_string = LLXmlTree::addAttributeString("width"); - if( !node->getFastAttributeS32( width_string, mWidth ) ) - { - return false; - } - - static LLStdStringHandle height_string = LLXmlTree::addAttributeString("height"); - if( !node->getFastAttributeS32( height_string, mHeight ) ) - { - return false; - } - - // Optional alpha component to apply after all compositing is complete. - static LLStdStringHandle alpha_tga_file_string = LLXmlTree::addAttributeString("alpha_tga_file"); - node->getFastAttributeString( alpha_tga_file_string, mStaticAlphaFileName ); - - static LLStdStringHandle clear_alpha_string = LLXmlTree::addAttributeString("clear_alpha"); - node->getFastAttributeBOOL( clear_alpha_string, mClearAlpha ); - - // <layer> - for (LLXmlTreeNode* child = node->getChildByName( "layer" ); - child; - child = node->getNextNamedChild()) - { - LLTexLayerInfo* info = new LLTexLayerInfo(); - if( !info->parseXml( child )) - { - delete info; - return false; - } - mLayerInfoList.push_back( info ); - } - return true; -} - -// creates visual params without generating layersets or layers -void LLTexLayerSetInfo::createVisualParams(LLAvatarAppearance *appearance) -{ - //layer_info_list_t mLayerInfoList; - for (LLTexLayerInfo* layer_info : mLayerInfoList) - { - layer_info->createVisualParams(appearance); - } -} - -//----------------------------------------------------------------------------- -// LLTexLayerSet -// An ordered set of texture layers that get composited into a single texture. -//----------------------------------------------------------------------------- - -bool LLTexLayerSet::sHasCaches = false; - -LLTexLayerSet::LLTexLayerSet(LLAvatarAppearance* const appearance) : - mAvatarAppearance( appearance ), - mIsVisible( true ), - mBakedTexIndex(LLAvatarAppearanceDefines::BAKED_HEAD), - mInfo( NULL ) -{ -} - -// virtual -LLTexLayerSet::~LLTexLayerSet() -{ - deleteCaches(); - std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer()); - mLayerList.clear(); - - std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer()); - mMaskLayerList.clear(); -} - -//----------------------------------------------------------------------------- -// setInfo -//----------------------------------------------------------------------------- - -bool LLTexLayerSet::setInfo(const LLTexLayerSetInfo *info) -{ - llassert(mInfo == NULL); - mInfo = info; - //mID = info->mID; // No ID - - mLayerList.reserve(info->mLayerInfoList.size()); - for (LLTexLayerInfo* layer_info : info->mLayerInfoList) - { - LLTexLayerInterface *layer = NULL; - if (layer_info->isUserSettable()) - { - layer = new LLTexLayerTemplate( this, getAvatarAppearance() ); - } - else - { - layer = new LLTexLayer(this); - } - // this is the first time this layer (of either type) is being created - make sure you add the parameters to the avatar appearance - if (!layer->setInfo(layer_info, NULL)) - { - mInfo = NULL; - return false; - } - if (!layer->isVisibilityMask()) - { - mLayerList.push_back( layer ); - } - else - { - mMaskLayerList.push_back(layer); - } - } - - requestUpdate(); - - stop_glerror(); - - return true; -} - -#if 0 // obsolete -//----------------------------------------------------------------------------- -// parseData -//----------------------------------------------------------------------------- - -bool LLTexLayerSet::parseData(LLXmlTreeNode* node) -{ - LLTexLayerSetInfo *info = new LLTexLayerSetInfo; - - if (!info->parseXml(node)) - { - delete info; - return false; - } - if (!setInfo(info)) - { - delete info; - return false; - } - return true; -} -#endif - -void LLTexLayerSet::deleteCaches() -{ - for(LLTexLayerInterface* layer : mLayerList) - { - layer->deleteCaches(); - } - for (LLTexLayerInterface* layer : mMaskLayerList) - { - layer->deleteCaches(); - } -} - - -bool LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target ) -{ - bool success = true; - mIsVisible = true; - - if (mMaskLayerList.size() > 0) - { - for (LLTexLayerInterface* layer : mMaskLayerList) - { - if (layer->isInvisibleAlphaMask()) - { - mIsVisible = false; - } - } - } - - LLGLSUIDefault gls_ui; - LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE); - gGL.setColorMask(true, true); - - // clear buffer area to ensure we don't pick up UI elements - { - gGL.flush(); - gAlphaMaskProgram.setMinimumAlpha(0.0f); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f( 0.f, 0.f, 0.f, 1.f ); - - gl_rect_2d_simple( width, height ); - - gGL.flush(); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - - if (mIsVisible) - { - // composite color layers - for(LLTexLayerInterface* layer : mLayerList) - { - if (layer->getRenderPass() == LLTexLayer::RP_COLOR) - { - gGL.flush(); - success &= layer->render(x, y, width, height, bound_target); - gGL.flush(); - } - } - - renderAlphaMaskTextures(x, y, width, height, bound_target, false); - - stop_glerror(); - } - else - { - gGL.flush(); - - gGL.setSceneBlendType(LLRender::BT_REPLACE); - gAlphaMaskProgram.setMinimumAlpha(0.f); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f( 0.f, 0.f, 0.f, 0.f ); - - gl_rect_2d_simple( width, height ); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - gGL.flush(); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - - return success; -} - - -bool LLTexLayerSet::isBodyRegion(const std::string& region) const -{ - return mInfo->mBodyRegion == region; -} - -const std::string LLTexLayerSet::getBodyRegionName() const -{ - return mInfo->mBodyRegion; -} - -void LLTexLayerSet::destroyComposite() -{ - if( mComposite ) - { - mComposite = NULL; - } -} - -LLTexLayerSetBuffer* LLTexLayerSet::getComposite() -{ - if (!mComposite) - { - createComposite(); - } - return mComposite; -} - -const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const -{ - return mComposite; -} - -void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height, LLRenderTarget* bound_target) -{ - LL_PROFILE_ZONE_SCOPED; - memset(data, 255, width * height); - - for(LLTexLayerInterface* layer : mLayerList) - { - layer->gatherAlphaMasks(data, origin_x, origin_y, width, height, bound_target); - } - - // Set alpha back to that of our alpha masks. - renderAlphaMaskTextures(origin_x, origin_y, width, height, bound_target, true); -} - -void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target, bool forceClear) -{ - LL_PROFILE_ZONE_SCOPED; - const LLTexLayerSetInfo *info = getInfo(); - - gGL.setColorMask(false, true); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - - // (Optionally) replace alpha with a single component image from a tga file. - if (!info->mStaticAlphaFileName.empty()) - { - gGL.flush(); - { - LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(info->mStaticAlphaFileName, true); - if( tex ) - { - LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->bind(tex); - gl_rect_2d_simple_tex( width, height ); - } - } - gGL.flush(); - } - else if (forceClear || info->mClearAlpha || (mMaskLayerList.size() > 0)) - { - // Set the alpha channel to one (clean up after previous blending) - gGL.flush(); - gAlphaMaskProgram.setMinimumAlpha(0.f); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f( 0.f, 0.f, 0.f, 1.f ); - - gl_rect_2d_simple( width, height ); - - gGL.flush(); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - - // (Optional) Mask out part of the baked texture with alpha masks - // will still have an effect even if mClearAlpha is set or the alpha component was replaced - if (mMaskLayerList.size() > 0) - { - gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA); - for (LLTexLayerInterface* layer : mMaskLayerList) - { - gGL.flush(); - layer->blendAlphaTexture(x,y,width, height); - gGL.flush(); - } - - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.setColorMask(true, true); - gGL.setSceneBlendType(LLRender::BT_ALPHA); -} - -void LLTexLayerSet::applyMorphMask(const U8* tex_data, S32 width, S32 height, S32 num_components) -{ - mAvatarAppearance->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex); -} - -bool LLTexLayerSet::isMorphValid() const -{ - for(const LLTexLayerInterface* layer : mLayerList) - { - if (layer && !layer->isMorphValid()) - { - return false; - } - } - return true; -} - -void LLTexLayerSet::invalidateMorphMasks() -{ - for(LLTexLayerInterface* layer : mLayerList) - { - if (layer) - { - layer->invalidateMorphMasks(); - } - } -} - - -//----------------------------------------------------------------------------- -// LLTexLayerInfo -//----------------------------------------------------------------------------- -LLTexLayerInfo::LLTexLayerInfo() : - mWriteAllChannels( false ), - mRenderPass(LLTexLayer::RP_COLOR), - mFixedColor( 0.f, 0.f, 0.f, 0.f ), - mLocalTexture( -1 ), - mStaticImageIsMask( false ), - mUseLocalTextureAlphaOnly(false), - mIsVisibilityMask(false) -{ -} - -LLTexLayerInfo::~LLTexLayerInfo( ) -{ - std::for_each(mParamColorInfoList.begin(), mParamColorInfoList.end(), DeletePointer()); - mParamColorInfoList.clear(); - std::for_each(mParamAlphaInfoList.begin(), mParamAlphaInfoList.end(), DeletePointer()); - mParamAlphaInfoList.clear(); -} - -bool LLTexLayerInfo::parseXml(LLXmlTreeNode* node) -{ - llassert( node->hasName( "layer" ) ); - - // name attribute - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if( !node->getFastAttributeString( name_string, mName ) ) - { - return false; - } - - static LLStdStringHandle write_all_channels_string = LLXmlTree::addAttributeString("write_all_channels"); - node->getFastAttributeBOOL( write_all_channels_string, mWriteAllChannels ); - - std::string render_pass_name; - static LLStdStringHandle render_pass_string = LLXmlTree::addAttributeString("render_pass"); - if( node->getFastAttributeString( render_pass_string, render_pass_name ) ) - { - if( render_pass_name == "bump" ) - { - mRenderPass = LLTexLayer::RP_BUMP; - } - } - - // Note: layers can have either a "global_color" attrib, a "fixed_color" attrib, or a <param_color> child. - // global color attribute (optional) - static LLStdStringHandle global_color_string = LLXmlTree::addAttributeString("global_color"); - node->getFastAttributeString( global_color_string, mGlobalColor ); - - // Visibility mask (optional) - bool is_visibility; - static LLStdStringHandle visibility_mask_string = LLXmlTree::addAttributeString("visibility_mask"); - if (node->getFastAttributeBOOL(visibility_mask_string, is_visibility)) - { - mIsVisibilityMask = is_visibility; - } - - // color attribute (optional) - LLColor4U color4u; - static LLStdStringHandle fixed_color_string = LLXmlTree::addAttributeString("fixed_color"); - if( node->getFastAttributeColor4U( fixed_color_string, color4u ) ) - { - mFixedColor.setVec( color4u ); - } - - // <texture> optional sub-element - for (LLXmlTreeNode* texture_node = node->getChildByName( "texture" ); - texture_node; - texture_node = node->getNextNamedChild()) - { - std::string local_texture_name; - static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file"); - static LLStdStringHandle local_texture_string = LLXmlTree::addAttributeString("local_texture"); - static LLStdStringHandle file_is_mask_string = LLXmlTree::addAttributeString("file_is_mask"); - static LLStdStringHandle local_texture_alpha_only_string = LLXmlTree::addAttributeString("local_texture_alpha_only"); - if( texture_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) ) - { - texture_node->getFastAttributeBOOL( file_is_mask_string, mStaticImageIsMask ); - } - else if (texture_node->getFastAttributeString(local_texture_string, local_texture_name)) - { - texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly ); - - /* if ("upper_shirt" == local_texture_name) - mLocalTexture = TEX_UPPER_SHIRT; */ - mLocalTexture = TEX_NUM_INDICES; - for (const LLAvatarAppearanceDictionary::Textures::value_type& dict_pair : LLAvatarAppearance::getDictionary()->getTextures()) - { - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = dict_pair.second; - if (local_texture_name == texture_dict->mName) - { - mLocalTexture = dict_pair.first; - break; - } - } - if (mLocalTexture == TEX_NUM_INDICES) - { - LL_WARNS() << "<texture> element has invalid local_texture attribute: " << mName << " " << local_texture_name << LL_ENDL; - return false; - } - } - else - { - LL_WARNS() << "<texture> element is missing a required attribute. " << mName << LL_ENDL; - return false; - } - } - - for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" ); - maskNode; - maskNode = node->getNextNamedChild()) - { - std::string morph_name; - static LLStdStringHandle morph_name_string = LLXmlTree::addAttributeString("morph_name"); - if (maskNode->getFastAttributeString(morph_name_string, morph_name)) - { - bool invert = false; - static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert"); - maskNode->getFastAttributeBOOL(invert_string, invert); - mMorphNameList.push_back(std::pair<std::string,bool>(morph_name,invert)); - } - } - - // <param> optional sub-element (color or alpha params) - for (LLXmlTreeNode* child = node->getChildByName( "param" ); - child; - child = node->getNextNamedChild()) - { - if( child->getChildByName( "param_color" ) ) - { - // <param><param_color/></param> - LLTexLayerParamColorInfo* info = new LLTexLayerParamColorInfo(); - if (!info->parseXml(child)) - { - delete info; - return false; - } - mParamColorInfoList.push_back(info); - } - else if( child->getChildByName( "param_alpha" ) ) - { - // <param><param_alpha/></param> - LLTexLayerParamAlphaInfo* info = new LLTexLayerParamAlphaInfo( ); - if (!info->parseXml(child)) - { - delete info; - return false; - } - mParamAlphaInfoList.push_back(info); - } - } - - return true; -} - -bool LLTexLayerInfo::createVisualParams(LLAvatarAppearance *appearance) -{ - bool success = true; - for (LLTexLayerParamColorInfo* color_info : mParamColorInfoList) - { - LLTexLayerParamColor* param_color = new LLTexLayerParamColor(appearance); - if (!param_color->setInfo(color_info, true)) - { - LL_WARNS() << "NULL TexLayer Color Param could not be added to visual param list. Deleting." << LL_ENDL; - delete param_color; - success = false; - } - } - - for (LLTexLayerParamAlphaInfo* alpha_info : mParamAlphaInfoList) - { - LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha(appearance); - if (!param_alpha->setInfo(alpha_info, true)) - { - LL_WARNS() << "NULL TexLayer Alpha Param could not be added to visual param list. Deleting." << LL_ENDL; - delete param_alpha; - success = false; - } - } - - return success; -} - -LLTexLayerInterface::LLTexLayerInterface(LLTexLayerSet* const layer_set): - mTexLayerSet( layer_set ), - mMorphMasksValid( false ), - mInfo(NULL), - mHasMorph(false) -{ -} - -LLTexLayerInterface::LLTexLayerInterface(const LLTexLayerInterface &layer, LLWearable *wearable): - mTexLayerSet( layer.mTexLayerSet ), - mInfo(NULL) -{ - // don't add visual params for cloned layers - setInfo(layer.getInfo(), wearable); - - mHasMorph = layer.mHasMorph; -} - -bool LLTexLayerInterface::setInfo(const LLTexLayerInfo *info, LLWearable* wearable ) // This sets mInfo and calls initialization functions -{ - // setInfo should only be called once. Code is not robust enough to handle redefinition of a texlayer. - // Not a critical warning, but could be useful for debugging later issues. -Nyx - if (mInfo != NULL) - { - LL_WARNS() << "mInfo != NULL" << LL_ENDL; - } - mInfo = info; - //mID = info->mID; // No ID - - mParamColorList.reserve(mInfo->mParamColorInfoList.size()); - for (LLTexLayerParamColorInfo* color_info : mInfo->mParamColorInfoList) - { - LLTexLayerParamColor* param_color; - if (!wearable) - { - param_color = new LLTexLayerParamColor(this); - if (!param_color->setInfo(color_info, true)) - { - mInfo = NULL; - return false; - } - } - else - { - param_color = (LLTexLayerParamColor*)wearable->getVisualParam(color_info->getID()); - if (!param_color) - { - mInfo = NULL; - return false; - } - } - mParamColorList.push_back( param_color ); - } - - mParamAlphaList.reserve(mInfo->mParamAlphaInfoList.size()); - for (LLTexLayerParamAlphaInfo* alpha_info : mInfo->mParamAlphaInfoList) - { - LLTexLayerParamAlpha* param_alpha; - if (!wearable) - { - param_alpha = new LLTexLayerParamAlpha( this ); - if (!param_alpha->setInfo(alpha_info, true)) - { - mInfo = NULL; - return false; - } - } - else - { - param_alpha = (LLTexLayerParamAlpha*) wearable->getVisualParam(alpha_info->getID()); - if (!param_alpha) - { - mInfo = NULL; - return false; - } - } - mParamAlphaList.push_back( param_alpha ); - } - - return true; -} - -/*virtual*/ void LLTexLayerInterface::requestUpdate() -{ - mTexLayerSet->requestUpdate(); -} - -const std::string& LLTexLayerInterface::getName() const -{ - return mInfo->mName; -} - -ETextureIndex LLTexLayerInterface::getLocalTextureIndex() const -{ - return (ETextureIndex) mInfo->mLocalTexture; -} - -LLWearableType::EType LLTexLayerInterface::getWearableType() const -{ - ETextureIndex te = getLocalTextureIndex(); - if (TEX_INVALID == te) - { - LLWearableType::EType type = LLWearableType::WT_INVALID; - - for (LLTexLayerParamColor* param : mParamColorList) - { - if (param) - { - LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType(); - if (new_type != LLWearableType::WT_INVALID && new_type != type) - { - if (type != LLWearableType::WT_INVALID) - { - return LLWearableType::WT_INVALID; - } - type = new_type; - } - } - } - - for (LLTexLayerParamAlpha* param : mParamAlphaList) - { - if (param) - { - LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType(); - if (new_type != LLWearableType::WT_INVALID && new_type != type) - { - if (type != LLWearableType::WT_INVALID) - { - return LLWearableType::WT_INVALID; - } - type = new_type; - } - } - } - - return type; - } - return LLAvatarAppearance::getDictionary()->getTEWearableType(te); -} - -LLTexLayerInterface::ERenderPass LLTexLayerInterface::getRenderPass() const -{ - return mInfo->mRenderPass; -} - -const std::string& LLTexLayerInterface::getGlobalColor() const -{ - return mInfo->mGlobalColor; -} - -bool LLTexLayerInterface::isVisibilityMask() const -{ - return mInfo->mIsVisibilityMask; -} - -void LLTexLayerInterface::invalidateMorphMasks() -{ - mMorphMasksValid = false; -} - -LLViewerVisualParam* LLTexLayerInterface::getVisualParamPtr(S32 index) const -{ - LLViewerVisualParam *result = NULL; - for (LLTexLayerParamColor* param : mParamColorList) - { - if (param->getID() == index) - { - result = param; - } - } - for (LLTexLayerParamAlpha* param : mParamAlphaList) - { - if (param->getID() == index) - { - result = param; - } - } - - return result; -} - -//----------------------------------------------------------------------------- -// LLTexLayer -// A single texture layer, consisting of: -// * color, consisting of either -// * one or more color parameters (weighted colors) -// * a reference to a global color -// * a fixed color with non-zero alpha -// * opaque white (the default) -// * (optional) a texture defined by either -// * a GUID -// * a texture entry index (TE) -// * (optional) one or more alpha parameters (weighted alpha textures) -//----------------------------------------------------------------------------- -LLTexLayer::LLTexLayer(LLTexLayerSet* const layer_set) : - LLTexLayerInterface( layer_set ), - mLocalTextureObject(NULL) -{ -} - -LLTexLayer::LLTexLayer(const LLTexLayer &layer, LLWearable *wearable) : - LLTexLayerInterface( layer, wearable ), - mLocalTextureObject(NULL) -{ -} - -LLTexLayer::LLTexLayer(const LLTexLayerTemplate &layer_template, LLLocalTextureObject *lto, LLWearable *wearable) : - LLTexLayerInterface( layer_template, wearable ), - mLocalTextureObject(lto) -{ -} - -LLTexLayer::~LLTexLayer() -{ - // mParamAlphaList and mParamColorList are LLViewerVisualParam's and get - // deleted with ~LLCharacter() - //std::for_each(mParamAlphaList.begin(), mParamAlphaList.end(), DeletePointer()); - //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer()); - - for (alpha_cache_t::value_type& alpha_pair : mAlphaCache) - { - U8* alpha_data = alpha_pair.second; - ll_aligned_free_32(alpha_data); - } - -} - -void LLTexLayer::asLLSD(LLSD& sd) const -{ - // *TODO: Finish - sd["id"] = getUUID(); -} - -//----------------------------------------------------------------------------- -// setInfo -//----------------------------------------------------------------------------- - -bool LLTexLayer::setInfo(const LLTexLayerInfo* info, LLWearable* wearable ) -{ - return LLTexLayerInterface::setInfo(info, wearable); -} - -//static -void LLTexLayer::calculateTexLayerColor(const param_color_list_t ¶m_list, LLColor4 &net_color) -{ - for (const LLTexLayerParamColor* param : param_list) - { - LLColor4 param_net = param->getNetColor(); - const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)param->getInfo(); - switch(info->getOperation()) - { - case LLTexLayerParamColor::OP_ADD: - net_color += param_net; - break; - case LLTexLayerParamColor::OP_MULTIPLY: - net_color = net_color * param_net; - break; - case LLTexLayerParamColor::OP_BLEND: - net_color = lerp(net_color, param_net, param->getWeight()); - break; - default: - llassert(0); - break; - } - } - net_color.clamp(); -} - -/*virtual*/ void LLTexLayer::deleteCaches() -{ - // Only need to delete caches for alpha params. Color params don't hold extra memory - for (LLTexLayerParamAlpha* param : mParamAlphaList) - { - param->deleteCaches(); - } -} - -bool LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target) -{ - // *TODO: Is this correct? - //gPipeline.disableLights(); - stop_glerror(); - - LLColor4 net_color; - bool color_specified = findNetColor(&net_color); - - if (mTexLayerSet->getAvatarAppearance()->mIsDummy) - { - color_specified = true; - net_color = LLAvatarAppearance::getDummyColor(); - } - - bool success = true; - - // If you can't see the layer, don't render it. - if( is_approx_zero( net_color.mV[VW] ) ) - { - return success; - } - - bool alpha_mask_specified = false; - param_alpha_list_t::const_iterator iter = mParamAlphaList.begin(); - if( iter != mParamAlphaList.end() ) - { - // If we have alpha masks, but we're skipping all of them, skip the whole layer. - // However, we can't do this optimization if we have morph masks that need updating. -/* if (!mHasMorph) - { - bool skip_layer = true; - - while( iter != mParamAlphaList.end() ) - { - const LLTexLayerParamAlpha* param = *iter; - - if( !param->getSkip() ) - { - skip_layer = false; - break; - } - - iter++; - } - - if( skip_layer ) - { - return success; - } - }//*/ - - const bool force_render = true; - renderMorphMasks(x, y, width, height, net_color, bound_target, force_render); - alpha_mask_specified = true; - gGL.flush(); - gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA); - } - - gGL.color4fv( net_color.mV); - - if( getInfo()->mWriteAllChannels ) - { - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - } - - if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly ) - { - { - LLGLTexture* tex = NULL; - if (mLocalTextureObject && mLocalTextureObject->getImage()) - { - tex = mLocalTextureObject->getImage(); - if (mLocalTextureObject->getID() == IMG_DEFAULT_AVATAR) - { - tex = NULL; - } - } - else - { - LL_INFOS() << "lto not defined or image not defined: " << getInfo()->getLocalTexture() << " lto: " << mLocalTextureObject << LL_ENDL; - } -// if( mTexLayerSet->getAvatarAppearance()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) ) - { - if( tex ) - { - bool no_alpha_test = getInfo()->mWriteAllChannels; - if (no_alpha_test) - { - gAlphaMaskProgram.setMinimumAlpha(0.f); - } - - LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); - - gGL.getTexUnit(0)->bind(tex, true); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - - gl_rect_2d_simple_tex( width, height ); - - gGL.getTexUnit(0)->setTextureAddressMode(old_mode); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (no_alpha_test) - { - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - } - } -// else -// { -// success = false; -// } - } - } - - if( !getInfo()->mStaticImageFileName.empty() ) - { - { - LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( tex ) - { - gGL.getTexUnit(0)->bind(tex, true); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - else - { - success = false; - } - } - } - - if(((-1 == getInfo()->mLocalTexture) || - getInfo()->mUseLocalTextureAlphaOnly) && - getInfo()->mStaticImageFileName.empty() && - color_specified ) - { - gAlphaMaskProgram.setMinimumAlpha(0.000f); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4fv( net_color.mV ); - gl_rect_2d_simple( width, height ); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - - if( alpha_mask_specified || getInfo()->mWriteAllChannels ) - { - // Restore standard blend func value - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - stop_glerror(); - } - - if( !success ) - { - LL_INFOS() << "LLTexLayer::render() partial: " << getInfo()->mName << LL_ENDL; - } - return success; -} - -const U8* LLTexLayer::getAlphaData() const -{ - LLCRC alpha_mask_crc; - const LLUUID& uuid = getUUID(); - alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES); - - for (const LLTexLayerParamAlpha* param : mParamAlphaList) - { - // MULTI-WEARABLE: verify visual parameters used here - F32 param_weight = param->getWeight(); - alpha_mask_crc.update((U8*)¶m_weight, sizeof(F32)); - } - - U32 cache_index = alpha_mask_crc.getCRC(); - - alpha_cache_t::const_iterator iter2 = mAlphaCache.find(cache_index); - return (iter2 == mAlphaCache.end()) ? 0 : iter2->second; -} - -bool LLTexLayer::findNetColor(LLColor4* net_color) const -{ - // Color is either: - // * one or more color parameters (weighted colors) (which may make use of a global color or fixed color) - // * a reference to a global color - // * a fixed color with non-zero alpha - // * opaque white (the default) - - if( !mParamColorList.empty() ) - { - if( !getGlobalColor().empty() ) - { - net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getInfo()->mGlobalColor ) ); - } - else if (getInfo()->mFixedColor.mV[VW]) - { - net_color->setVec( getInfo()->mFixedColor ); - } - else - { - net_color->setVec( 0.f, 0.f, 0.f, 0.f ); - } - - calculateTexLayerColor(mParamColorList, *net_color); - return true; - } - - if( !getGlobalColor().empty() ) - { - net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getGlobalColor() ) ); - return true; - } - - if( getInfo()->mFixedColor.mV[VW] ) - { - net_color->setVec( getInfo()->mFixedColor ); - return true; - } - - net_color->setToWhite(); - - return false; // No need to draw a separate colored polygon -} - -bool LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) -{ - bool success = true; - - gGL.flush(); - - if( !getInfo()->mStaticImageFileName.empty() ) - { - LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask ); - if( tex ) - { - gAlphaMaskProgram.setMinimumAlpha(0.f); - gGL.getTexUnit(0)->bind(tex, true); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - else - { - success = false; - } - } - else - { - if (getInfo()->mLocalTexture >=0 && getInfo()->mLocalTexture < TEX_NUM_INDICES) - { - LLGLTexture* tex = mLocalTextureObject->getImage(); - if (tex) - { - gAlphaMaskProgram.setMinimumAlpha(0.f); - gGL.getTexUnit(0)->bind(tex); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gAlphaMaskProgram.setMinimumAlpha(0.004f); - } - } - } - - return success; -} - -/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target) -{ - addAlphaMask(data, originX, originY, width, height, bound_target); -} - -void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, LLRenderTarget* bound_target, bool force_render) -{ - if (!force_render && !hasMorph()) - { - LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL; - return; - } - LL_PROFILE_ZONE_SCOPED; - bool success = true; - - llassert( !mParamAlphaList.empty() ); - - gAlphaMaskProgram.setMinimumAlpha(0.f); - gGL.setColorMask(false, true); - - LLTexLayerParamAlpha* first_param = *mParamAlphaList.begin(); - // Note: if the first param is a mulitply, multiply against the current buffer's alpha - if( !first_param || !first_param->getMultiplyBlend() ) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Clear the alpha - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - - gGL.color4f( 0.f, 0.f, 0.f, 0.f ); - gl_rect_2d_simple( width, height ); - } - - // Accumulate alphas - gGL.color4f( 1.f, 1.f, 1.f, 1.f ); - for (LLTexLayerParamAlpha* param : mParamAlphaList) - { - success &= param->render( x, y, width, height ); - if (!success && !force_render) - { - LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL; - return; - } - } - - // Approximates a min() function - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA); - - // Accumulate the alpha component of the texture - if( getInfo()->mLocalTexture != -1 ) - { - LLGLTexture* tex = mLocalTextureObject->getImage(); - if( tex && (tex->getComponents() == 4) ) - { - LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); - - gGL.getTexUnit(0)->bind(tex, true); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - - gl_rect_2d_simple_tex( width, height ); - - gGL.getTexUnit(0)->setTextureAddressMode(old_mode); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - } - - if( !getInfo()->mStaticImageFileName.empty() && getInfo()->mStaticImageIsMask ) - { - LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( tex ) - { - if( (tex->getComponents() == 4) || (tex->getComponents() == 1) ) - { - gGL.getTexUnit(0)->bind(tex, true); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - else - { - LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName - << "; expected 1 or 4 components." << LL_ENDL; - } - } - } - - // Draw a rectangle with the layer color to multiply the alpha by that color's alpha. - // Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO ); - if ( !is_approx_equal(layer_color.mV[VW], 1.f) ) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4fv(layer_color.mV); - gl_rect_2d_simple( width, height ); - } - - gAlphaMaskProgram.setMinimumAlpha(0.004f); - - LLGLSUIDefault gls_ui; - - gGL.setColorMask(true, true); - - if (hasMorph() && success) - { - LLCRC alpha_mask_crc; - const LLUUID& uuid = getUUID(); - alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES); - - for (const LLTexLayerParamAlpha* param : mParamAlphaList) - { - F32 param_weight = param->getWeight(); - alpha_mask_crc.update((U8*)¶m_weight, sizeof(F32)); - } - - U32 cache_index = alpha_mask_crc.getCRC(); - U8* alpha_data = NULL; - // We believe we need to generate morph masks, do not assume that the cached version is accurate. - // We can get bad morph masks during login, on minimize, and occasional gl errors. - // We should only be doing this when we believe something has changed with respect to the user's appearance. - { - LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL; - // clear out a slot if we have filled our cache - S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1; - while ((S32)mAlphaCache.size() >= max_cache_entries) - { - alpha_cache_t::iterator iter2 = mAlphaCache.begin(); // arbitrarily grab the first entry - alpha_data = iter2->second; - ll_aligned_free_32(alpha_data); - mAlphaCache.erase(iter2); - } - - // GPUs tend to be very uptight about memory alignment as the DMA used to convey - // said data to the card works better when well-aligned so plain old default-aligned heap mem is a no-no - //new U8[width * height]; - size_t bytes_per_pixel = 1; // unsigned byte alpha channel only... - size_t row_size = (width + 3) & ~0x3; // OpenGL 4-byte row align (even for things < 4 bpp...) - size_t pixels = (row_size * height); - size_t mem_size = pixels * bytes_per_pixel; - - alpha_data = (U8*)ll_aligned_malloc_32(mem_size); - - bool skip_readback = LLRender::sNsightDebugSupport; // nSight doesn't support use of glReadPixels - - if (!skip_readback) - { - if (gGLManager.mIsIntel) - { // work-around for broken intel drivers which cannot do glReadPixels on an RGBA FBO - // returning only the alpha portion without locking up downstream - U8* temp = (U8*)ll_aligned_malloc_32(mem_size << 2); // allocate same size, but RGBA - - if (bound_target) - { - gGL.getTexUnit(0)->bind(bound_target); - } - else - { - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, 0); - } - - glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp); - - U8* alpha_cursor = alpha_data; - U8* pixel = temp; - for (int i = 0; i < pixels; i++) - { - *alpha_cursor++ = pixel[3]; - pixel += 4; - } - - gGL.getTexUnit(0)->disable(); - - ll_aligned_free_32(temp); - } - else - { // platforms with working drivers... - // We just want GL_ALPHA, but that isn't supported in OGL core profile 4. - static const size_t TEMP_BYTES_PER_PIXEL = 4; - U8* temp_data = (U8*)ll_aligned_malloc_32(mem_size * TEMP_BYTES_PER_PIXEL); - glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, temp_data); - for (size_t pixel = 0; pixel < pixels; pixel++) { - alpha_data[pixel] = temp_data[(pixel * TEMP_BYTES_PER_PIXEL) + 3]; - } - ll_aligned_free_32(temp_data); - } - } - else - { - ll_aligned_free_32(alpha_data); - alpha_data = nullptr; - } - - mAlphaCache[cache_index] = alpha_data; - } - - getTexLayerSet()->getAvatarAppearance()->dirtyMesh(); - - mMorphMasksValid = true; - getTexLayerSet()->applyMorphMask(alpha_data, width, height, 1); - } -} - -void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target) -{ - LL_PROFILE_ZONE_SCOPED; - S32 size = width * height; - const U8* alphaData = getAlphaData(); - if (!alphaData && hasAlphaParams()) - { - LLColor4 net_color; - findNetColor( &net_color ); - // TODO: eliminate need for layer morph mask valid flag - invalidateMorphMasks(); - const bool force_render = false; - renderMorphMasks(originX, originY, width, height, net_color, bound_target, force_render); - alphaData = getAlphaData(); - } - if (alphaData) - { - for( S32 i = 0; i < size; i++ ) - { - U8 curAlpha = data[i]; - U16 resultAlpha = curAlpha; - resultAlpha *= ( ((U16)alphaData[i]) + 1); - resultAlpha = resultAlpha >> 8; - data[i] = (U8)resultAlpha; - } - } -} - -/*virtual*/ bool LLTexLayer::isInvisibleAlphaMask() const -{ - if (mLocalTextureObject) - { - if (mLocalTextureObject->getID() == IMG_INVISIBLE) - { - return true; - } - } - - return false; -} - -LLUUID LLTexLayer::getUUID() const -{ - LLUUID uuid; - if( getInfo()->mLocalTexture != -1 ) - { - LLGLTexture* tex = mLocalTextureObject->getImage(); - if (tex) - { - uuid = mLocalTextureObject->getID(); - } - } - if( !getInfo()->mStaticImageFileName.empty() ) - { - LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( tex ) - { - uuid = tex->getID(); - } - } - return uuid; -} - - -//----------------------------------------------------------------------------- -// LLTexLayerTemplate -// A single texture layer, consisting of: -// * color, consisting of either -// * one or more color parameters (weighted colors) -// * a reference to a global color -// * a fixed color with non-zero alpha -// * opaque white (the default) -// * (optional) a texture defined by either -// * a GUID -// * a texture entry index (TE) -// * (optional) one or more alpha parameters (weighted alpha textures) -//----------------------------------------------------------------------------- -LLTexLayerTemplate::LLTexLayerTemplate(LLTexLayerSet* layer_set, LLAvatarAppearance* const appearance) : - LLTexLayerInterface(layer_set), - mAvatarAppearance( appearance ) -{ -} - -LLTexLayerTemplate::LLTexLayerTemplate(const LLTexLayerTemplate &layer) : - LLTexLayerInterface(layer), - mAvatarAppearance(layer.getAvatarAppearance()) -{ -} - -LLTexLayerTemplate::~LLTexLayerTemplate() -{ -} - -//----------------------------------------------------------------------------- -// setInfo -//----------------------------------------------------------------------------- - -/*virtual*/ bool LLTexLayerTemplate::setInfo(const LLTexLayerInfo* info, LLWearable* wearable ) -{ - return LLTexLayerInterface::setInfo(info, wearable); -} - -U32 LLTexLayerTemplate::updateWearableCache() const -{ - mWearableCache.clear(); - - LLWearableType::EType wearable_type = getWearableType(); - if (LLWearableType::WT_INVALID == wearable_type) - { - //this isn't a cloneable layer - return 0; - } - U32 num_wearables = getAvatarAppearance()->getWearableData()->getWearableCount(wearable_type); - U32 added = 0; - for (U32 i = 0; i < num_wearables; i++) - { - LLWearable* wearable = getAvatarAppearance()->getWearableData()->getWearable(wearable_type, i); - if (!wearable) - { - continue; - } - mWearableCache.push_back(wearable); - added++; - } - return added; -} -LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) const -{ - if (mWearableCache.size() <= i) - { - return NULL; - } - LLWearable *wearable = mWearableCache[i]; - LLLocalTextureObject *lto = NULL; - LLTexLayer *layer = NULL; - if (wearable) - { - lto = wearable->getLocalTextureObject(mInfo->mLocalTexture); - } - if (lto) - { - layer = lto->getTexLayer(getName()); - } - return layer; -} - -/*virtual*/ bool LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target) -{ - if(!mInfo) - { - return false ; - } - - bool success = true; - updateWearableCache(); - for (LLWearable* wearable : mWearableCache) - { - LLLocalTextureObject *lto = NULL; - LLTexLayer *layer = NULL; - if (wearable) - { - lto = wearable->getLocalTextureObject(mInfo->mLocalTexture); - } - if (lto) - { - layer = lto->getTexLayer(getName()); - } - if (layer) - { - wearable->writeToAvatar(mAvatarAppearance); - layer->setLTO(lto); - success &= layer->render(x, y, width, height, bound_target); - } - } - - return success; -} - -/*virtual*/ bool LLTexLayerTemplate::blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) // Multiplies a single alpha texture against the frame buffer -{ - bool success = true; - U32 num_wearables = updateWearableCache(); - for (U32 i = 0; i < num_wearables; i++) - { - LLTexLayer *layer = getLayer(i); - if (layer) - { - success &= layer->blendAlphaTexture(x,y,width,height); - } - } - return success; -} - -/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target) -{ - U32 num_wearables = updateWearableCache(); - U32 i = num_wearables - 1; // For rendering morph masks, we only want to use the top wearable - LLTexLayer *layer = getLayer(i); - if (layer) - { - layer->addAlphaMask(data, originX, originY, width, height, bound_target); - } -} - -/*virtual*/ void LLTexLayerTemplate::setHasMorph(bool newval) -{ - mHasMorph = newval; - U32 num_wearables = updateWearableCache(); - for (U32 i = 0; i < num_wearables; i++) - { - LLTexLayer *layer = getLayer(i); - if (layer) - { - layer->setHasMorph(newval); - } - } -} - -/*virtual*/ void LLTexLayerTemplate::deleteCaches() -{ - U32 num_wearables = updateWearableCache(); - for (U32 i = 0; i < num_wearables; i++) - { - LLTexLayer *layer = getLayer(i); - if (layer) - { - layer->deleteCaches(); - } - } -} - -/*virtual*/ bool LLTexLayerTemplate::isInvisibleAlphaMask() const -{ - U32 num_wearables = updateWearableCache(); - for (U32 i = 0; i < num_wearables; i++) - { - LLTexLayer *layer = getLayer(i); - if (layer) - { - if (layer->isInvisibleAlphaMask()) - { - return true; - } - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// finds a specific layer based on a passed in name -//----------------------------------------------------------------------------- -LLTexLayerInterface* LLTexLayerSet::findLayerByName(const std::string& name) -{ - for (LLTexLayerInterface* layer : mLayerList) - { - if (layer->getName() == name) - { - return layer; - } - } - for (LLTexLayerInterface* layer : mMaskLayerList) - { - if (layer->getName() == name) - { - return layer; - } - } - return NULL; -} - -void LLTexLayerSet::cloneTemplates(LLLocalTextureObject *lto, LLAvatarAppearanceDefines::ETextureIndex tex_index, LLWearable *wearable) -{ - // initialize all texlayers with this texture type for this LTO - for(LLTexLayerInterface* layer : mLayerList) - { - LLTexLayerTemplate* layer_template = (LLTexLayerTemplate*)layer; - if (layer_template->getInfo()->getLocalTexture() == (S32)tex_index) - { - lto->addTexLayer(layer_template, wearable); - } - } - for(LLTexLayerInterface* layer : mMaskLayerList) - { - LLTexLayerTemplate* layer_template = (LLTexLayerTemplate*)layer; - if (layer_template->getInfo()->getLocalTexture() == (S32)tex_index) - { - lto->addTexLayer(layer_template, wearable); - } - } -} -//----------------------------------------------------------------------------- -// LLTexLayerStaticImageList -//----------------------------------------------------------------------------- - -LLTexLayerStaticImageList::LLTexLayerStaticImageList() : - mGLBytes(0), - mTGABytes(0), - mImageNames(16384) -{ -} - -LLTexLayerStaticImageList::~LLTexLayerStaticImageList() -{ - deleteCachedImages(); -} - -void LLTexLayerStaticImageList::dumpByteCount() const -{ - LL_INFOS() << "Avatar Static Textures " << - "KB GL:" << (mGLBytes / 1024) << - "KB TGA:" << (mTGABytes / 1024) << "KB" << LL_ENDL; -} - -void LLTexLayerStaticImageList::deleteCachedImages() -{ - if( mGLBytes || mTGABytes ) - { - LL_INFOS() << "Clearing Static Textures " << - "KB GL:" << (mGLBytes / 1024) << - "KB TGA:" << (mTGABytes / 1024) << "KB" << LL_ENDL; - - //mStaticImageLists uses LLPointers, clear() will cause deletion - - mStaticImageListTGA.clear(); - mStaticImageList.clear(); - - mGLBytes = 0; - mTGABytes = 0; - } -} - -// Note: in general, for a given image image we'll call either getImageTga() or getTexture(). -// We call getImageTga() if the image is used as an alpha gradient. -// Otherwise, we call getTexture() - -// Returns an LLImageTGA that contains the encoded data from a tga file named file_name. -// Caches the result to speed identical subsequent requests. -LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name) -{ - LL_PROFILE_ZONE_SCOPED; - const char *namekey = mImageNames.addString(file_name); - image_tga_map_t::const_iterator iter = mStaticImageListTGA.find(namekey); - if( iter != mStaticImageListTGA.end() ) - { - return iter->second; - } - else - { - std::string path; - path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name); - LLPointer<LLImageTGA> image_tga = new LLImageTGA( path ); - if( image_tga->getDataSize() > 0 ) - { - mStaticImageListTGA[ namekey ] = image_tga; - mTGABytes += image_tga->getDataSize(); - return image_tga; - } - else - { - return NULL; - } - } -} - -// Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name. -// Caches the result to speed identical subsequent requests. -LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, bool is_mask) -{ - LL_PROFILE_ZONE_SCOPED; - LLPointer<LLGLTexture> tex; - const char *namekey = mImageNames.addString(file_name); - - texture_map_t::const_iterator iter = mStaticImageList.find(namekey); - if( iter != mStaticImageList.end() ) - { - tex = iter->second; - } - else - { - llassert(gTextureManagerBridgep); - tex = gTextureManagerBridgep->getLocalTexture( false ); - LLPointer<LLImageRaw> image_raw = new LLImageRaw; - if( loadImageRaw( file_name, image_raw ) ) - { - if( (image_raw->getComponents() == 1) && is_mask ) - { - // Convert grayscale alpha masks from single channel into RGBA. - // Fill RGB with black to allow fixed function gl calls - // to match shader implementation. - LLPointer<LLImageRaw> alpha_image_raw = image_raw; - image_raw = new LLImageRaw(image_raw->getWidth(), - image_raw->getHeight(), - 4); - - image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black); - } - tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL); - - gGL.getTexUnit(0)->bind(tex); - tex->setAddressMode(LLTexUnit::TAM_CLAMP); - - mStaticImageList [ namekey ] = tex; - mGLBytes += (S32)tex->getWidth() * tex->getHeight() * tex->getComponents(); - } - else - { - tex = NULL; - } - } - - return tex; -} - -// Reads a .tga file, decodes it, and puts the decoded data in image_raw. -// Returns true if successful. -bool LLTexLayerStaticImageList::loadImageRaw(const std::string& file_name, LLImageRaw* image_raw) -{ - LL_PROFILE_ZONE_SCOPED; - bool success = false; - std::string path; - path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name); - LLPointer<LLImageTGA> image_tga = new LLImageTGA( path ); - if( image_tga->getDataSize() > 0 ) - { - // Copy data from tga to raw. - success = image_tga->decode( image_raw ); - } - - return success; -} - +/**
+ * @file lltexlayer.cpp
+ * @brief A texture layer. Used for avatars.
+ *
+ * $LicenseInfo:firstyear=2002&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 "linden_common.h"
+
+#include "lltexlayer.h"
+
+#include "llavatarappearance.h"
+#include "llcrc.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "lldir.h"
+#include "lltexlayerparams.h"
+#include "lltexturemanagerbridge.h"
+#include "lllocaltextureobject.h"
+#include "../llui/llui.h"
+#include "llwearable.h"
+#include "llwearabledata.h"
+#include "llvertexbuffer.h"
+#include "llviewervisualparam.h"
+#include "llfasttimer.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+using namespace LLAvatarAppearanceDefines;
+
+// runway consolidate
+extern std::string self_av_string();
+
+class LLTexLayerInfo
+{
+ friend class LLTexLayer;
+ friend class LLTexLayerTemplate;
+ friend class LLTexLayerInterface;
+public:
+ LLTexLayerInfo();
+ ~LLTexLayerInfo();
+
+ bool parseXml(LLXmlTreeNode* node);
+ bool createVisualParams(LLAvatarAppearance *appearance);
+ bool isUserSettable() { return mLocalTexture != -1; }
+ S32 getLocalTexture() const { return mLocalTexture; }
+ bool getOnlyAlpha() const { return mUseLocalTextureAlphaOnly; }
+ std::string getName() const { return mName; }
+
+private:
+ std::string mName;
+
+ bool mWriteAllChannels; // Don't use masking. Just write RGBA into buffer,
+ LLTexLayerInterface::ERenderPass mRenderPass;
+
+ std::string mGlobalColor;
+ LLColor4 mFixedColor;
+
+ S32 mLocalTexture;
+ std::string mStaticImageFileName;
+ bool mStaticImageIsMask;
+ bool mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask
+ bool mIsVisibilityMask;
+
+ typedef std::vector< std::pair< std::string,bool > > morph_name_list_t;
+ morph_name_list_t mMorphNameList;
+ param_color_info_list_t mParamColorInfoList;
+ param_alpha_info_list_t mParamAlphaInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLViewerTexLayerSet writes to. Each LLViewerTexLayerSet has one.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner) :
+ mTexLayerSet(owner)
+{
+}
+
+LLTexLayerSetBuffer::~LLTexLayerSetBuffer()
+{
+}
+
+void LLTexLayerSetBuffer::pushProjection() const
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ gGL.ortho(0.0f, getCompositeWidth(), 0.0f, getCompositeHeight(), -1.0f, 1.0f);
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+}
+
+void LLTexLayerSetBuffer::popProjection() const
+{
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.popMatrix();
+}
+
+// virtual
+void LLTexLayerSetBuffer::preRenderTexLayerSet()
+{
+ // Set up an ortho projection
+ pushProjection();
+}
+
+// virtual
+void LLTexLayerSetBuffer::postRenderTexLayerSet(bool success)
+{
+ popProjection();
+}
+
+bool LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target)
+{
+ // Default color mask for tex layer render
+ gGL.setColorMask(true, true);
+
+ bool success = true;
+
+ gAlphaMaskProgram.bind();
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+
+ LLVertexBuffer::unbind();
+
+ // Composite the color data
+ LLGLSUIDefault gls_ui;
+ success &= mTexLayerSet->render( getCompositeOriginX(), getCompositeOriginY(),
+ getCompositeWidth(), getCompositeHeight(), bound_target );
+ gGL.flush();
+
+ midRenderTexLayerSet(success);
+
+ gAlphaMaskProgram.unbind();
+
+ LLVertexBuffer::unbind();
+
+ // reset GL state
+ gGL.setColorMask(true, true);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetInfo
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetInfo::LLTexLayerSetInfo() :
+ mBodyRegion( "" ),
+ mWidth( 512 ),
+ mHeight( 512 ),
+ mClearAlpha( true )
+{
+}
+
+LLTexLayerSetInfo::~LLTexLayerSetInfo( )
+{
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+ mLayerInfoList.clear();
+}
+
+bool LLTexLayerSetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer_set" ) );
+ if( !node->hasName( "layer_set" ) )
+ {
+ return false;
+ }
+
+ // body_region
+ static LLStdStringHandle body_region_string = LLXmlTree::addAttributeString("body_region");
+ if( !node->getFastAttributeString( body_region_string, mBodyRegion ) )
+ {
+ LL_WARNS() << "<layer_set> is missing body_region attribute" << LL_ENDL;
+ return false;
+ }
+
+ // width, height
+ static LLStdStringHandle width_string = LLXmlTree::addAttributeString("width");
+ if( !node->getFastAttributeS32( width_string, mWidth ) )
+ {
+ return false;
+ }
+
+ static LLStdStringHandle height_string = LLXmlTree::addAttributeString("height");
+ if( !node->getFastAttributeS32( height_string, mHeight ) )
+ {
+ return false;
+ }
+
+ // Optional alpha component to apply after all compositing is complete.
+ static LLStdStringHandle alpha_tga_file_string = LLXmlTree::addAttributeString("alpha_tga_file");
+ node->getFastAttributeString( alpha_tga_file_string, mStaticAlphaFileName );
+
+ static LLStdStringHandle clear_alpha_string = LLXmlTree::addAttributeString("clear_alpha");
+ node->getFastAttributeBOOL( clear_alpha_string, mClearAlpha );
+
+ // <layer>
+ for (LLXmlTreeNode* child = node->getChildByName( "layer" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLTexLayerInfo* info = new LLTexLayerInfo();
+ if( !info->parseXml( child ))
+ {
+ delete info;
+ return false;
+ }
+ mLayerInfoList.push_back( info );
+ }
+ return true;
+}
+
+// creates visual params without generating layersets or layers
+void LLTexLayerSetInfo::createVisualParams(LLAvatarAppearance *appearance)
+{
+ //layer_info_list_t mLayerInfoList;
+ for (LLTexLayerInfo* layer_info : mLayerInfoList)
+ {
+ layer_info->createVisualParams(appearance);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+bool LLTexLayerSet::sHasCaches = false;
+
+LLTexLayerSet::LLTexLayerSet(LLAvatarAppearance* const appearance) :
+ mAvatarAppearance( appearance ),
+ mIsVisible( true ),
+ mBakedTexIndex(LLAvatarAppearanceDefines::BAKED_HEAD),
+ mInfo( NULL )
+{
+}
+
+// virtual
+LLTexLayerSet::~LLTexLayerSet()
+{
+ deleteCaches();
+ std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer());
+ mLayerList.clear();
+
+ std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer());
+ mMaskLayerList.clear();
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+bool LLTexLayerSet::setInfo(const LLTexLayerSetInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ mLayerList.reserve(info->mLayerInfoList.size());
+ for (LLTexLayerInfo* layer_info : info->mLayerInfoList)
+ {
+ LLTexLayerInterface *layer = NULL;
+ if (layer_info->isUserSettable())
+ {
+ layer = new LLTexLayerTemplate( this, getAvatarAppearance() );
+ }
+ else
+ {
+ layer = new LLTexLayer(this);
+ }
+ // this is the first time this layer (of either type) is being created - make sure you add the parameters to the avatar appearance
+ if (!layer->setInfo(layer_info, NULL))
+ {
+ mInfo = NULL;
+ return false;
+ }
+ if (!layer->isVisibilityMask())
+ {
+ mLayerList.push_back( layer );
+ }
+ else
+ {
+ mMaskLayerList.push_back(layer);
+ }
+ }
+
+ requestUpdate();
+
+ stop_glerror();
+
+ return true;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+
+bool LLTexLayerSet::parseData(LLXmlTreeNode* node)
+{
+ LLTexLayerSetInfo *info = new LLTexLayerSetInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return false;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return false;
+ }
+ return true;
+}
+#endif
+
+void LLTexLayerSet::deleteCaches()
+{
+ for(LLTexLayerInterface* layer : mLayerList)
+ {
+ layer->deleteCaches();
+ }
+ for (LLTexLayerInterface* layer : mMaskLayerList)
+ {
+ layer->deleteCaches();
+ }
+}
+
+
+bool LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target )
+{
+ bool success = true;
+ mIsVisible = true;
+
+ if (mMaskLayerList.size() > 0)
+ {
+ for (LLTexLayerInterface* layer : mMaskLayerList)
+ {
+ if (layer->isInvisibleAlphaMask())
+ {
+ mIsVisible = false;
+ }
+ }
+ }
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+ gGL.setColorMask(true, true);
+
+ // clear buffer area to ensure we don't pick up UI elements
+ {
+ gGL.flush();
+ gAlphaMaskProgram.setMinimumAlpha(0.0f);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 1.f );
+
+ gl_rect_2d_simple( width, height );
+
+ gGL.flush();
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+
+ if (mIsVisible)
+ {
+ // composite color layers
+ for(LLTexLayerInterface* layer : mLayerList)
+ {
+ if (layer->getRenderPass() == LLTexLayer::RP_COLOR)
+ {
+ gGL.flush();
+ success &= layer->render(x, y, width, height, bound_target);
+ gGL.flush();
+ }
+ }
+
+ renderAlphaMaskTextures(x, y, width, height, bound_target, false);
+
+ stop_glerror();
+ }
+ else
+ {
+ gGL.flush();
+
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 0.f );
+
+ gl_rect_2d_simple( width, height );
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ gGL.flush();
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+
+ return success;
+}
+
+
+bool LLTexLayerSet::isBodyRegion(const std::string& region) const
+{
+ return mInfo->mBodyRegion == region;
+}
+
+const std::string LLTexLayerSet::getBodyRegionName() const
+{
+ return mInfo->mBodyRegion;
+}
+
+void LLTexLayerSet::destroyComposite()
+{
+ if( mComposite )
+ {
+ mComposite = NULL;
+ }
+}
+
+LLTexLayerSetBuffer* LLTexLayerSet::getComposite()
+{
+ if (!mComposite)
+ {
+ createComposite();
+ }
+ return mComposite;
+}
+
+const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const
+{
+ return mComposite;
+}
+
+void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ memset(data, 255, width * height);
+
+ for(LLTexLayerInterface* layer : mLayerList)
+ {
+ layer->gatherAlphaMasks(data, origin_x, origin_y, width, height, bound_target);
+ }
+
+ // Set alpha back to that of our alpha masks.
+ renderAlphaMaskTextures(origin_x, origin_y, width, height, bound_target, true);
+}
+
+void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target, bool forceClear)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ const LLTexLayerSetInfo *info = getInfo();
+
+ gGL.setColorMask(false, true);
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+
+ // (Optionally) replace alpha with a single component image from a tga file.
+ if (!info->mStaticAlphaFileName.empty())
+ {
+ gGL.flush();
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(info->mStaticAlphaFileName, true);
+ if( tex )
+ {
+ LLGLSUIDefault gls_ui;
+ gGL.getTexUnit(0)->bind(tex);
+ gl_rect_2d_simple_tex( width, height );
+ }
+ }
+ gGL.flush();
+ }
+ else if (forceClear || info->mClearAlpha || (mMaskLayerList.size() > 0))
+ {
+ // Set the alpha channel to one (clean up after previous blending)
+ gGL.flush();
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f( 0.f, 0.f, 0.f, 1.f );
+
+ gl_rect_2d_simple( width, height );
+
+ gGL.flush();
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+
+ // (Optional) Mask out part of the baked texture with alpha masks
+ // will still have an effect even if mClearAlpha is set or the alpha component was replaced
+ if (mMaskLayerList.size() > 0)
+ {
+ gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA);
+ for (LLTexLayerInterface* layer : mMaskLayerList)
+ {
+ gGL.flush();
+ layer->blendAlphaTexture(x,y,width, height);
+ gGL.flush();
+ }
+
+ }
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.setColorMask(true, true);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+}
+
+void LLTexLayerSet::applyMorphMask(const U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ mAvatarAppearance->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex);
+}
+
+bool LLTexLayerSet::isMorphValid() const
+{
+ for(const LLTexLayerInterface* layer : mLayerList)
+ {
+ if (layer && !layer->isMorphValid())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void LLTexLayerSet::invalidateMorphMasks()
+{
+ for(LLTexLayerInterface* layer : mLayerList)
+ {
+ if (layer)
+ {
+ layer->invalidateMorphMasks();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+LLTexLayerInfo::LLTexLayerInfo() :
+ mWriteAllChannels( false ),
+ mRenderPass(LLTexLayer::RP_COLOR),
+ mFixedColor( 0.f, 0.f, 0.f, 0.f ),
+ mLocalTexture( -1 ),
+ mStaticImageIsMask( false ),
+ mUseLocalTextureAlphaOnly(false),
+ mIsVisibilityMask(false)
+{
+}
+
+LLTexLayerInfo::~LLTexLayerInfo( )
+{
+ std::for_each(mParamColorInfoList.begin(), mParamColorInfoList.end(), DeletePointer());
+ mParamColorInfoList.clear();
+ std::for_each(mParamAlphaInfoList.begin(), mParamAlphaInfoList.end(), DeletePointer());
+ mParamAlphaInfoList.clear();
+}
+
+bool LLTexLayerInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer" ) );
+
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ return false;
+ }
+
+ static LLStdStringHandle write_all_channels_string = LLXmlTree::addAttributeString("write_all_channels");
+ node->getFastAttributeBOOL( write_all_channels_string, mWriteAllChannels );
+
+ std::string render_pass_name;
+ static LLStdStringHandle render_pass_string = LLXmlTree::addAttributeString("render_pass");
+ if( node->getFastAttributeString( render_pass_string, render_pass_name ) )
+ {
+ if( render_pass_name == "bump" )
+ {
+ mRenderPass = LLTexLayer::RP_BUMP;
+ }
+ }
+
+ // Note: layers can have either a "global_color" attrib, a "fixed_color" attrib, or a <param_color> child.
+ // global color attribute (optional)
+ static LLStdStringHandle global_color_string = LLXmlTree::addAttributeString("global_color");
+ node->getFastAttributeString( global_color_string, mGlobalColor );
+
+ // Visibility mask (optional)
+ bool is_visibility;
+ static LLStdStringHandle visibility_mask_string = LLXmlTree::addAttributeString("visibility_mask");
+ if (node->getFastAttributeBOOL(visibility_mask_string, is_visibility))
+ {
+ mIsVisibilityMask = is_visibility;
+ }
+
+ // color attribute (optional)
+ LLColor4U color4u;
+ static LLStdStringHandle fixed_color_string = LLXmlTree::addAttributeString("fixed_color");
+ if( node->getFastAttributeColor4U( fixed_color_string, color4u ) )
+ {
+ mFixedColor.setVec( color4u );
+ }
+
+ // <texture> optional sub-element
+ for (LLXmlTreeNode* texture_node = node->getChildByName( "texture" );
+ texture_node;
+ texture_node = node->getNextNamedChild())
+ {
+ std::string local_texture_name;
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ static LLStdStringHandle local_texture_string = LLXmlTree::addAttributeString("local_texture");
+ static LLStdStringHandle file_is_mask_string = LLXmlTree::addAttributeString("file_is_mask");
+ static LLStdStringHandle local_texture_alpha_only_string = LLXmlTree::addAttributeString("local_texture_alpha_only");
+ if( texture_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ texture_node->getFastAttributeBOOL( file_is_mask_string, mStaticImageIsMask );
+ }
+ else if (texture_node->getFastAttributeString(local_texture_string, local_texture_name))
+ {
+ texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly );
+
+ /* if ("upper_shirt" == local_texture_name)
+ mLocalTexture = TEX_UPPER_SHIRT; */
+ mLocalTexture = TEX_NUM_INDICES;
+ for (const LLAvatarAppearanceDictionary::Textures::value_type& dict_pair : LLAvatarAppearance::getDictionary()->getTextures())
+ {
+ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = dict_pair.second;
+ if (local_texture_name == texture_dict->mName)
+ {
+ mLocalTexture = dict_pair.first;
+ break;
+ }
+ }
+ if (mLocalTexture == TEX_NUM_INDICES)
+ {
+ LL_WARNS() << "<texture> element has invalid local_texture attribute: " << mName << " " << local_texture_name << LL_ENDL;
+ return false;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "<texture> element is missing a required attribute. " << mName << LL_ENDL;
+ return false;
+ }
+ }
+
+ for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" );
+ maskNode;
+ maskNode = node->getNextNamedChild())
+ {
+ std::string morph_name;
+ static LLStdStringHandle morph_name_string = LLXmlTree::addAttributeString("morph_name");
+ if (maskNode->getFastAttributeString(morph_name_string, morph_name))
+ {
+ bool invert = false;
+ static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert");
+ maskNode->getFastAttributeBOOL(invert_string, invert);
+ mMorphNameList.push_back(std::pair<std::string,bool>(morph_name,invert));
+ }
+ }
+
+ // <param> optional sub-element (color or alpha params)
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexLayerParamColorInfo* info = new LLTexLayerParamColorInfo();
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return false;
+ }
+ mParamColorInfoList.push_back(info);
+ }
+ else if( child->getChildByName( "param_alpha" ) )
+ {
+ // <param><param_alpha/></param>
+ LLTexLayerParamAlphaInfo* info = new LLTexLayerParamAlphaInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return false;
+ }
+ mParamAlphaInfoList.push_back(info);
+ }
+ }
+
+ return true;
+}
+
+bool LLTexLayerInfo::createVisualParams(LLAvatarAppearance *appearance)
+{
+ bool success = true;
+ for (LLTexLayerParamColorInfo* color_info : mParamColorInfoList)
+ {
+ LLTexLayerParamColor* param_color = new LLTexLayerParamColor(appearance);
+ if (!param_color->setInfo(color_info, true))
+ {
+ LL_WARNS() << "NULL TexLayer Color Param could not be added to visual param list. Deleting." << LL_ENDL;
+ delete param_color;
+ success = false;
+ }
+ }
+
+ for (LLTexLayerParamAlphaInfo* alpha_info : mParamAlphaInfoList)
+ {
+ LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha(appearance);
+ if (!param_alpha->setInfo(alpha_info, true))
+ {
+ LL_WARNS() << "NULL TexLayer Alpha Param could not be added to visual param list. Deleting." << LL_ENDL;
+ delete param_alpha;
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+LLTexLayerInterface::LLTexLayerInterface(LLTexLayerSet* const layer_set):
+ mTexLayerSet( layer_set ),
+ mMorphMasksValid( false ),
+ mInfo(NULL),
+ mHasMorph(false)
+{
+}
+
+LLTexLayerInterface::LLTexLayerInterface(const LLTexLayerInterface &layer, LLWearable *wearable):
+ mTexLayerSet( layer.mTexLayerSet ),
+ mInfo(NULL)
+{
+ // don't add visual params for cloned layers
+ setInfo(layer.getInfo(), wearable);
+
+ mHasMorph = layer.mHasMorph;
+}
+
+bool LLTexLayerInterface::setInfo(const LLTexLayerInfo *info, LLWearable* wearable ) // This sets mInfo and calls initialization functions
+{
+ // setInfo should only be called once. Code is not robust enough to handle redefinition of a texlayer.
+ // Not a critical warning, but could be useful for debugging later issues. -Nyx
+ if (mInfo != NULL)
+ {
+ LL_WARNS() << "mInfo != NULL" << LL_ENDL;
+ }
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ mParamColorList.reserve(mInfo->mParamColorInfoList.size());
+ for (LLTexLayerParamColorInfo* color_info : mInfo->mParamColorInfoList)
+ {
+ LLTexLayerParamColor* param_color;
+ if (!wearable)
+ {
+ param_color = new LLTexLayerParamColor(this);
+ if (!param_color->setInfo(color_info, true))
+ {
+ mInfo = NULL;
+ return false;
+ }
+ }
+ else
+ {
+ param_color = (LLTexLayerParamColor*)wearable->getVisualParam(color_info->getID());
+ if (!param_color)
+ {
+ mInfo = NULL;
+ return false;
+ }
+ }
+ mParamColorList.push_back( param_color );
+ }
+
+ mParamAlphaList.reserve(mInfo->mParamAlphaInfoList.size());
+ for (LLTexLayerParamAlphaInfo* alpha_info : mInfo->mParamAlphaInfoList)
+ {
+ LLTexLayerParamAlpha* param_alpha;
+ if (!wearable)
+ {
+ param_alpha = new LLTexLayerParamAlpha( this );
+ if (!param_alpha->setInfo(alpha_info, true))
+ {
+ mInfo = NULL;
+ return false;
+ }
+ }
+ else
+ {
+ param_alpha = (LLTexLayerParamAlpha*) wearable->getVisualParam(alpha_info->getID());
+ if (!param_alpha)
+ {
+ mInfo = NULL;
+ return false;
+ }
+ }
+ mParamAlphaList.push_back( param_alpha );
+ }
+
+ return true;
+}
+
+/*virtual*/ void LLTexLayerInterface::requestUpdate()
+{
+ mTexLayerSet->requestUpdate();
+}
+
+const std::string& LLTexLayerInterface::getName() const
+{
+ return mInfo->mName;
+}
+
+ETextureIndex LLTexLayerInterface::getLocalTextureIndex() const
+{
+ return (ETextureIndex) mInfo->mLocalTexture;
+}
+
+LLWearableType::EType LLTexLayerInterface::getWearableType() const
+{
+ ETextureIndex te = getLocalTextureIndex();
+ if (TEX_INVALID == te)
+ {
+ LLWearableType::EType type = LLWearableType::WT_INVALID;
+
+ for (LLTexLayerParamColor* param : mParamColorList)
+ {
+ if (param)
+ {
+ LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
+ if (new_type != LLWearableType::WT_INVALID && new_type != type)
+ {
+ if (type != LLWearableType::WT_INVALID)
+ {
+ return LLWearableType::WT_INVALID;
+ }
+ type = new_type;
+ }
+ }
+ }
+
+ for (LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ if (param)
+ {
+ LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
+ if (new_type != LLWearableType::WT_INVALID && new_type != type)
+ {
+ if (type != LLWearableType::WT_INVALID)
+ {
+ return LLWearableType::WT_INVALID;
+ }
+ type = new_type;
+ }
+ }
+ }
+
+ return type;
+ }
+ return LLAvatarAppearance::getDictionary()->getTEWearableType(te);
+}
+
+LLTexLayerInterface::ERenderPass LLTexLayerInterface::getRenderPass() const
+{
+ return mInfo->mRenderPass;
+}
+
+const std::string& LLTexLayerInterface::getGlobalColor() const
+{
+ return mInfo->mGlobalColor;
+}
+
+bool LLTexLayerInterface::isVisibilityMask() const
+{
+ return mInfo->mIsVisibilityMask;
+}
+
+void LLTexLayerInterface::invalidateMorphMasks()
+{
+ mMorphMasksValid = false;
+}
+
+LLViewerVisualParam* LLTexLayerInterface::getVisualParamPtr(S32 index) const
+{
+ LLViewerVisualParam *result = NULL;
+ for (LLTexLayerParamColor* param : mParamColorList)
+ {
+ if (param->getID() == index)
+ {
+ result = param;
+ }
+ }
+ for (LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ if (param->getID() == index)
+ {
+ result = param;
+ }
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayer
+// A single texture layer, consisting of:
+// * color, consisting of either
+// * one or more color parameters (weighted colors)
+// * a reference to a global color
+// * a fixed color with non-zero alpha
+// * opaque white (the default)
+// * (optional) a texture defined by either
+// * a GUID
+// * a texture entry index (TE)
+// * (optional) one or more alpha parameters (weighted alpha textures)
+//-----------------------------------------------------------------------------
+LLTexLayer::LLTexLayer(LLTexLayerSet* const layer_set) :
+ LLTexLayerInterface( layer_set ),
+ mLocalTextureObject(NULL)
+{
+}
+
+LLTexLayer::LLTexLayer(const LLTexLayer &layer, LLWearable *wearable) :
+ LLTexLayerInterface( layer, wearable ),
+ mLocalTextureObject(NULL)
+{
+}
+
+LLTexLayer::LLTexLayer(const LLTexLayerTemplate &layer_template, LLLocalTextureObject *lto, LLWearable *wearable) :
+ LLTexLayerInterface( layer_template, wearable ),
+ mLocalTextureObject(lto)
+{
+}
+
+LLTexLayer::~LLTexLayer()
+{
+ // mParamAlphaList and mParamColorList are LLViewerVisualParam's and get
+ // deleted with ~LLCharacter()
+ //std::for_each(mParamAlphaList.begin(), mParamAlphaList.end(), DeletePointer());
+ //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer());
+
+ for (alpha_cache_t::value_type& alpha_pair : mAlphaCache)
+ {
+ U8* alpha_data = alpha_pair.second;
+ ll_aligned_free_32(alpha_data);
+ }
+
+}
+
+void LLTexLayer::asLLSD(LLSD& sd) const
+{
+ // *TODO: Finish
+ sd["id"] = getUUID();
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+bool LLTexLayer::setInfo(const LLTexLayerInfo* info, LLWearable* wearable )
+{
+ return LLTexLayerInterface::setInfo(info, wearable);
+}
+
+//static
+void LLTexLayer::calculateTexLayerColor(const param_color_list_t ¶m_list, LLColor4 &net_color)
+{
+ for (const LLTexLayerParamColor* param : param_list)
+ {
+ LLColor4 param_net = param->getNetColor();
+ const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)param->getInfo();
+ switch(info->getOperation())
+ {
+ case LLTexLayerParamColor::OP_ADD:
+ net_color += param_net;
+ break;
+ case LLTexLayerParamColor::OP_MULTIPLY:
+ net_color = net_color * param_net;
+ break;
+ case LLTexLayerParamColor::OP_BLEND:
+ net_color = lerp(net_color, param_net, param->getWeight());
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ net_color.clamp();
+}
+
+/*virtual*/ void LLTexLayer::deleteCaches()
+{
+ // Only need to delete caches for alpha params. Color params don't hold extra memory
+ for (LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ param->deleteCaches();
+ }
+}
+
+bool LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ // *TODO: Is this correct?
+ //gPipeline.disableLights();
+ stop_glerror();
+
+ LLColor4 net_color;
+ bool color_specified = findNetColor(&net_color);
+
+ if (mTexLayerSet->getAvatarAppearance()->mIsDummy)
+ {
+ color_specified = true;
+ net_color = LLAvatarAppearance::getDummyColor();
+ }
+
+ bool success = true;
+
+ // If you can't see the layer, don't render it.
+ if( is_approx_zero( net_color.mV[VW] ) )
+ {
+ return success;
+ }
+
+ bool alpha_mask_specified = false;
+ param_alpha_list_t::const_iterator iter = mParamAlphaList.begin();
+ if( iter != mParamAlphaList.end() )
+ {
+ // If we have alpha masks, but we're skipping all of them, skip the whole layer.
+ // However, we can't do this optimization if we have morph masks that need updating.
+/* if (!mHasMorph)
+ {
+ bool skip_layer = true;
+
+ while( iter != mParamAlphaList.end() )
+ {
+ const LLTexLayerParamAlpha* param = *iter;
+
+ if( !param->getSkip() )
+ {
+ skip_layer = false;
+ break;
+ }
+
+ iter++;
+ }
+
+ if( skip_layer )
+ {
+ return success;
+ }
+ }//*/
+
+ const bool force_render = true;
+ renderMorphMasks(x, y, width, height, net_color, bound_target, force_render);
+ alpha_mask_specified = true;
+ gGL.flush();
+ gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA);
+ }
+
+ gGL.color4fv( net_color.mV);
+
+ if( getInfo()->mWriteAllChannels )
+ {
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+ }
+
+ if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly )
+ {
+ {
+ LLGLTexture* tex = NULL;
+ if (mLocalTextureObject && mLocalTextureObject->getImage())
+ {
+ tex = mLocalTextureObject->getImage();
+ if (mLocalTextureObject->getID() == IMG_DEFAULT_AVATAR)
+ {
+ tex = NULL;
+ }
+ }
+ else
+ {
+ LL_INFOS() << "lto not defined or image not defined: " << getInfo()->getLocalTexture() << " lto: " << mLocalTextureObject << LL_ENDL;
+ }
+// if( mTexLayerSet->getAvatarAppearance()->getLocalTextureGL((ETextureIndex)getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( tex )
+ {
+ bool no_alpha_test = getInfo()->mWriteAllChannels;
+ if (no_alpha_test)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ }
+
+ LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
+
+ gGL.getTexUnit(0)->bind(tex, true);
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ if (no_alpha_test)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+ }
+// else
+// {
+// success = false;
+// }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ gGL.getTexUnit(0)->bind(tex, true);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ }
+
+ if(((-1 == getInfo()->mLocalTexture) ||
+ getInfo()->mUseLocalTextureAlphaOnly) &&
+ getInfo()->mStaticImageFileName.empty() &&
+ color_specified )
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.000f);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4fv( net_color.mV );
+ gl_rect_2d_simple( width, height );
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+
+ if( alpha_mask_specified || getInfo()->mWriteAllChannels )
+ {
+ // Restore standard blend func value
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ stop_glerror();
+ }
+
+ if( !success )
+ {
+ LL_INFOS() << "LLTexLayer::render() partial: " << getInfo()->mName << LL_ENDL;
+ }
+ return success;
+}
+
+const U8* LLTexLayer::getAlphaData() const
+{
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = getUUID();
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for (const LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ // MULTI-WEARABLE: verify visual parameters used here
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)¶m_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+
+ alpha_cache_t::const_iterator iter2 = mAlphaCache.find(cache_index);
+ return (iter2 == mAlphaCache.end()) ? 0 : iter2->second;
+}
+
+bool LLTexLayer::findNetColor(LLColor4* net_color) const
+{
+ // Color is either:
+ // * one or more color parameters (weighted colors) (which may make use of a global color or fixed color)
+ // * a reference to a global color
+ // * a fixed color with non-zero alpha
+ // * opaque white (the default)
+
+ if( !mParamColorList.empty() )
+ {
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getInfo()->mGlobalColor ) );
+ }
+ else if (getInfo()->mFixedColor.mV[VW])
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ }
+ else
+ {
+ net_color->setVec( 0.f, 0.f, 0.f, 0.f );
+ }
+
+ calculateTexLayerColor(mParamColorList, *net_color);
+ return true;
+ }
+
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatarAppearance()->getGlobalColor( getGlobalColor() ) );
+ return true;
+ }
+
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ return true;
+ }
+
+ net_color->setToWhite();
+
+ return false; // No need to draw a separate colored polygon
+}
+
+bool LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
+{
+ bool success = true;
+
+ gGL.flush();
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( tex )
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ gGL.getTexUnit(0)->bind(tex, true);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ else
+ {
+ if (getInfo()->mLocalTexture >=0 && getInfo()->mLocalTexture < TEX_NUM_INDICES)
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if (tex)
+ {
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ gGL.getTexUnit(0)->bind(tex);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+ }
+ }
+ }
+
+ return success;
+}
+
+/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ addAlphaMask(data, originX, originY, width, height, bound_target);
+}
+
+void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, LLRenderTarget* bound_target, bool force_render)
+{
+ if (!force_render && !hasMorph())
+ {
+ LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
+ return;
+ }
+ LL_PROFILE_ZONE_SCOPED;
+ bool success = true;
+
+ llassert( !mParamAlphaList.empty() );
+
+ gAlphaMaskProgram.setMinimumAlpha(0.f);
+ gGL.setColorMask(false, true);
+
+ LLTexLayerParamAlpha* first_param = *mParamAlphaList.begin();
+ // Note: if the first param is a mulitply, multiply against the current buffer's alpha
+ if( !first_param || !first_param->getMultiplyBlend() )
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // Clear the alpha
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+
+ gGL.color4f( 0.f, 0.f, 0.f, 0.f );
+ gl_rect_2d_simple( width, height );
+ }
+
+ // Accumulate alphas
+ gGL.color4f( 1.f, 1.f, 1.f, 1.f );
+ for (LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ success &= param->render( x, y, width, height );
+ if (!success && !force_render)
+ {
+ LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
+ return;
+ }
+ }
+
+ // Approximates a min() function
+ gGL.flush();
+ gGL.setSceneBlendType(LLRender::BT_MULT_ALPHA);
+
+ // Accumulate the alpha component of the texture
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if( tex && (tex->getComponents() == 4) )
+ {
+ LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
+
+ gGL.getTexUnit(0)->bind(tex, true);
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ gGL.getTexUnit(0)->setTextureAddressMode(old_mode);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() && getInfo()->mStaticImageIsMask )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ if( (tex->getComponents() == 4) || (tex->getComponents() == 1) )
+ {
+ gGL.getTexUnit(0)->bind(tex, true);
+ gl_rect_2d_simple_tex( width, height );
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ }
+ else
+ {
+ LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
+ << "; expected 1 or 4 components." << LL_ENDL;
+ }
+ }
+ }
+
+ // Draw a rectangle with the layer color to multiply the alpha by that color's alpha.
+ // Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO );
+ if ( !is_approx_equal(layer_color.mV[VW], 1.f) )
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4fv(layer_color.mV);
+ gl_rect_2d_simple( width, height );
+ }
+
+ gAlphaMaskProgram.setMinimumAlpha(0.004f);
+
+ LLGLSUIDefault gls_ui;
+
+ gGL.setColorMask(true, true);
+
+ if (hasMorph() && success)
+ {
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = getUUID();
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for (const LLTexLayerParamAlpha* param : mParamAlphaList)
+ {
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)¶m_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+ U8* alpha_data = NULL;
+ // We believe we need to generate morph masks, do not assume that the cached version is accurate.
+ // We can get bad morph masks during login, on minimize, and occasional gl errors.
+ // We should only be doing this when we believe something has changed with respect to the user's appearance.
+ {
+ LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
+ // clear out a slot if we have filled our cache
+ S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
+ while ((S32)mAlphaCache.size() >= max_cache_entries)
+ {
+ alpha_cache_t::iterator iter2 = mAlphaCache.begin(); // arbitrarily grab the first entry
+ alpha_data = iter2->second;
+ ll_aligned_free_32(alpha_data);
+ mAlphaCache.erase(iter2);
+ }
+
+ // GPUs tend to be very uptight about memory alignment as the DMA used to convey
+ // said data to the card works better when well-aligned so plain old default-aligned heap mem is a no-no
+ //new U8[width * height];
+ size_t bytes_per_pixel = 1; // unsigned byte alpha channel only...
+ size_t row_size = (width + 3) & ~0x3; // OpenGL 4-byte row align (even for things < 4 bpp...)
+ size_t pixels = (row_size * height);
+ size_t mem_size = pixels * bytes_per_pixel;
+
+ alpha_data = (U8*)ll_aligned_malloc_32(mem_size);
+
+ bool skip_readback = LLRender::sNsightDebugSupport; // nSight doesn't support use of glReadPixels
+
+ if (!skip_readback)
+ {
+ if (gGLManager.mIsIntel)
+ { // work-around for broken intel drivers which cannot do glReadPixels on an RGBA FBO
+ // returning only the alpha portion without locking up downstream
+ U8* temp = (U8*)ll_aligned_malloc_32(mem_size << 2); // allocate same size, but RGBA
+
+ if (bound_target)
+ {
+ gGL.getTexUnit(0)->bind(bound_target);
+ }
+ else
+ {
+ gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, 0);
+ }
+
+ glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
+
+ U8* alpha_cursor = alpha_data;
+ U8* pixel = temp;
+ for (int i = 0; i < pixels; i++)
+ {
+ *alpha_cursor++ = pixel[3];
+ pixel += 4;
+ }
+
+ gGL.getTexUnit(0)->disable();
+
+ ll_aligned_free_32(temp);
+ }
+ else
+ { // platforms with working drivers...
+ // We just want GL_ALPHA, but that isn't supported in OGL core profile 4.
+ static const size_t TEMP_BYTES_PER_PIXEL = 4;
+ U8* temp_data = (U8*)ll_aligned_malloc_32(mem_size * TEMP_BYTES_PER_PIXEL);
+ glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, temp_data);
+ for (size_t pixel = 0; pixel < pixels; pixel++) {
+ alpha_data[pixel] = temp_data[(pixel * TEMP_BYTES_PER_PIXEL) + 3];
+ }
+ ll_aligned_free_32(temp_data);
+ }
+ }
+ else
+ {
+ ll_aligned_free_32(alpha_data);
+ alpha_data = nullptr;
+ }
+
+ mAlphaCache[cache_index] = alpha_data;
+ }
+
+ getTexLayerSet()->getAvatarAppearance()->dirtyMesh();
+
+ mMorphMasksValid = true;
+ getTexLayerSet()->applyMorphMask(alpha_data, width, height, 1);
+ }
+}
+
+void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ S32 size = width * height;
+ const U8* alphaData = getAlphaData();
+ if (!alphaData && hasAlphaParams())
+ {
+ LLColor4 net_color;
+ findNetColor( &net_color );
+ // TODO: eliminate need for layer morph mask valid flag
+ invalidateMorphMasks();
+ const bool force_render = false;
+ renderMorphMasks(originX, originY, width, height, net_color, bound_target, force_render);
+ alphaData = getAlphaData();
+ }
+ if (alphaData)
+ {
+ for( S32 i = 0; i < size; i++ )
+ {
+ U8 curAlpha = data[i];
+ U16 resultAlpha = curAlpha;
+ resultAlpha *= ( ((U16)alphaData[i]) + 1);
+ resultAlpha = resultAlpha >> 8;
+ data[i] = (U8)resultAlpha;
+ }
+ }
+}
+
+/*virtual*/ bool LLTexLayer::isInvisibleAlphaMask() const
+{
+ if (mLocalTextureObject)
+ {
+ if (mLocalTextureObject->getID() == IMG_INVISIBLE)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+LLUUID LLTexLayer::getUUID() const
+{
+ LLUUID uuid;
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ LLGLTexture* tex = mLocalTextureObject->getImage();
+ if (tex)
+ {
+ uuid = mLocalTextureObject->getID();
+ }
+ }
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
+ if( tex )
+ {
+ uuid = tex->getID();
+ }
+ }
+ return uuid;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerTemplate
+// A single texture layer, consisting of:
+// * color, consisting of either
+// * one or more color parameters (weighted colors)
+// * a reference to a global color
+// * a fixed color with non-zero alpha
+// * opaque white (the default)
+// * (optional) a texture defined by either
+// * a GUID
+// * a texture entry index (TE)
+// * (optional) one or more alpha parameters (weighted alpha textures)
+//-----------------------------------------------------------------------------
+LLTexLayerTemplate::LLTexLayerTemplate(LLTexLayerSet* layer_set, LLAvatarAppearance* const appearance) :
+ LLTexLayerInterface(layer_set),
+ mAvatarAppearance( appearance )
+{
+}
+
+LLTexLayerTemplate::LLTexLayerTemplate(const LLTexLayerTemplate &layer) :
+ LLTexLayerInterface(layer),
+ mAvatarAppearance(layer.getAvatarAppearance())
+{
+}
+
+LLTexLayerTemplate::~LLTexLayerTemplate()
+{
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+/*virtual*/ bool LLTexLayerTemplate::setInfo(const LLTexLayerInfo* info, LLWearable* wearable )
+{
+ return LLTexLayerInterface::setInfo(info, wearable);
+}
+
+U32 LLTexLayerTemplate::updateWearableCache() const
+{
+ mWearableCache.clear();
+
+ LLWearableType::EType wearable_type = getWearableType();
+ if (LLWearableType::WT_INVALID == wearable_type)
+ {
+ //this isn't a cloneable layer
+ return 0;
+ }
+ U32 num_wearables = getAvatarAppearance()->getWearableData()->getWearableCount(wearable_type);
+ U32 added = 0;
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLWearable* wearable = getAvatarAppearance()->getWearableData()->getWearable(wearable_type, i);
+ if (!wearable)
+ {
+ continue;
+ }
+ mWearableCache.push_back(wearable);
+ added++;
+ }
+ return added;
+}
+LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) const
+{
+ if (mWearableCache.size() <= i)
+ {
+ return NULL;
+ }
+ LLWearable *wearable = mWearableCache[i];
+ LLLocalTextureObject *lto = NULL;
+ LLTexLayer *layer = NULL;
+ if (wearable)
+ {
+ lto = wearable->getLocalTextureObject(mInfo->mLocalTexture);
+ }
+ if (lto)
+ {
+ layer = lto->getTexLayer(getName());
+ }
+ return layer;
+}
+
+/*virtual*/ bool LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ if(!mInfo)
+ {
+ return false ;
+ }
+
+ bool success = true;
+ updateWearableCache();
+ for (LLWearable* wearable : mWearableCache)
+ {
+ LLLocalTextureObject *lto = NULL;
+ LLTexLayer *layer = NULL;
+ if (wearable)
+ {
+ lto = wearable->getLocalTextureObject(mInfo->mLocalTexture);
+ }
+ if (lto)
+ {
+ layer = lto->getTexLayer(getName());
+ }
+ if (layer)
+ {
+ wearable->writeToAvatar(mAvatarAppearance);
+ layer->setLTO(lto);
+ success &= layer->render(x, y, width, height, bound_target);
+ }
+ }
+
+ return success;
+}
+
+/*virtual*/ bool LLTexLayerTemplate::blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) // Multiplies a single alpha texture against the frame buffer
+{
+ bool success = true;
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ success &= layer->blendAlphaTexture(x,y,width,height);
+ }
+ }
+ return success;
+}
+
+/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, LLRenderTarget* bound_target)
+{
+ U32 num_wearables = updateWearableCache();
+ U32 i = num_wearables - 1; // For rendering morph masks, we only want to use the top wearable
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ layer->addAlphaMask(data, originX, originY, width, height, bound_target);
+ }
+}
+
+/*virtual*/ void LLTexLayerTemplate::setHasMorph(bool newval)
+{
+ mHasMorph = newval;
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ layer->setHasMorph(newval);
+ }
+ }
+}
+
+/*virtual*/ void LLTexLayerTemplate::deleteCaches()
+{
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ layer->deleteCaches();
+ }
+ }
+}
+
+/*virtual*/ bool LLTexLayerTemplate::isInvisibleAlphaMask() const
+{
+ U32 num_wearables = updateWearableCache();
+ for (U32 i = 0; i < num_wearables; i++)
+ {
+ LLTexLayer *layer = getLayer(i);
+ if (layer)
+ {
+ if (layer->isInvisibleAlphaMask())
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// finds a specific layer based on a passed in name
+//-----------------------------------------------------------------------------
+LLTexLayerInterface* LLTexLayerSet::findLayerByName(const std::string& name)
+{
+ for (LLTexLayerInterface* layer : mLayerList)
+ {
+ if (layer->getName() == name)
+ {
+ return layer;
+ }
+ }
+ for (LLTexLayerInterface* layer : mMaskLayerList)
+ {
+ if (layer->getName() == name)
+ {
+ return layer;
+ }
+ }
+ return NULL;
+}
+
+void LLTexLayerSet::cloneTemplates(LLLocalTextureObject *lto, LLAvatarAppearanceDefines::ETextureIndex tex_index, LLWearable *wearable)
+{
+ // initialize all texlayers with this texture type for this LTO
+ for(LLTexLayerInterface* layer : mLayerList)
+ {
+ LLTexLayerTemplate* layer_template = (LLTexLayerTemplate*)layer;
+ if (layer_template->getInfo()->getLocalTexture() == (S32)tex_index)
+ {
+ lto->addTexLayer(layer_template, wearable);
+ }
+ }
+ for(LLTexLayerInterface* layer : mMaskLayerList)
+ {
+ LLTexLayerTemplate* layer_template = (LLTexLayerTemplate*)layer;
+ if (layer_template->getInfo()->getLocalTexture() == (S32)tex_index)
+ {
+ lto->addTexLayer(layer_template, wearable);
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// LLTexLayerStaticImageList
+//-----------------------------------------------------------------------------
+
+LLTexLayerStaticImageList::LLTexLayerStaticImageList() :
+ mGLBytes(0),
+ mTGABytes(0),
+ mImageNames(16384)
+{
+}
+
+LLTexLayerStaticImageList::~LLTexLayerStaticImageList()
+{
+ deleteCachedImages();
+}
+
+void LLTexLayerStaticImageList::dumpByteCount() const
+{
+ LL_INFOS() << "Avatar Static Textures " <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << LL_ENDL;
+}
+
+void LLTexLayerStaticImageList::deleteCachedImages()
+{
+ if( mGLBytes || mTGABytes )
+ {
+ LL_INFOS() << "Clearing Static Textures " <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << LL_ENDL;
+
+ //mStaticImageLists uses LLPointers, clear() will cause deletion
+
+ mStaticImageListTGA.clear();
+ mStaticImageList.clear();
+
+ mGLBytes = 0;
+ mTGABytes = 0;
+ }
+}
+
+// Note: in general, for a given image image we'll call either getImageTga() or getTexture().
+// We call getImageTga() if the image is used as an alpha gradient.
+// Otherwise, we call getTexture()
+
+// Returns an LLImageTGA that contains the encoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageTGA* LLTexLayerStaticImageList::getImageTGA(const std::string& file_name)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ const char *namekey = mImageNames.addString(file_name);
+ image_tga_map_t::const_iterator iter = mStaticImageListTGA.find(namekey);
+ if( iter != mStaticImageListTGA.end() )
+ {
+ return iter->second;
+ }
+ else
+ {
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name);
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ mStaticImageListTGA[ namekey ] = image_tga;
+ mTGABytes += image_tga->getDataSize();
+ return image_tga;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+// Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name, bool is_mask)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ LLPointer<LLGLTexture> tex;
+ const char *namekey = mImageNames.addString(file_name);
+
+ texture_map_t::const_iterator iter = mStaticImageList.find(namekey);
+ if( iter != mStaticImageList.end() )
+ {
+ tex = iter->second;
+ }
+ else
+ {
+ llassert(gTextureManagerBridgep);
+ tex = gTextureManagerBridgep->getLocalTexture( false );
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ if( (image_raw->getComponents() == 1) && is_mask )
+ {
+ // Convert grayscale alpha masks from single channel into RGBA.
+ // Fill RGB with black to allow fixed function gl calls
+ // to match shader implementation.
+ LLPointer<LLImageRaw> alpha_image_raw = image_raw;
+ image_raw = new LLImageRaw(image_raw->getWidth(),
+ image_raw->getHeight(),
+ 4);
+
+ image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black);
+ }
+ tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL);
+
+ gGL.getTexUnit(0)->bind(tex);
+ tex->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ mStaticImageList [ namekey ] = tex;
+ mGLBytes += (S32)tex->getWidth() * tex->getHeight() * tex->getComponents();
+ }
+ else
+ {
+ tex = NULL;
+ }
+ }
+
+ return tex;
+}
+
+// Reads a .tga file, decodes it, and puts the decoded data in image_raw.
+// Returns true if successful.
+bool LLTexLayerStaticImageList::loadImageRaw(const std::string& file_name, LLImageRaw* image_raw)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ bool success = false;
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name);
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ // Copy data from tga to raw.
+ success = image_tga->decode( image_raw );
+ }
+
+ return success;
+}
+
|