Skip to content
Snippets Groups Projects
lltexlayer.cpp 52.8 KiB
Newer Older
James Cook's avatar
James Cook committed
/** 
 * @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$
James Cook's avatar
James Cook committed
 */

#include "linden_common.h"
James Cook's avatar
James Cook committed
#include "lltexlayer.h"
#include "llavatarappearance.h"
#include "llcrc.h"
#include "imageids.h"
#include "llimagej2c.h"
#include "llimagetga.h"
#include "lltexlayerparams.h"
#include "lltexturemanagerbridge.h"
prep's avatar
prep committed
#include "../llui/llui.h"
#include "llwearabledata.h"
James Cook's avatar
James Cook committed

//#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;
};

James Cook's avatar
James Cook committed
//-----------------------------------------------------------------------------
// LLTexLayerSetBuffer
// The composite image that a LLViewerTexLayerSet writes to.  Each LLViewerTexLayerSet has one.
James Cook's avatar
James Cook committed
//-----------------------------------------------------------------------------
LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner) :
James Cook's avatar
James Cook committed
{
void LLTexLayerSetBuffer::pushProjection() const
James Cook's avatar
James Cook committed
{
	gGL.matrixMode(LLRender::MM_PROJECTION);
	gGL.ortho(0.0f, getCompositeWidth(), 0.0f, getCompositeHeight(), -1.0f, 1.0f);
James Cook's avatar
James Cook committed

	gGL.matrixMode(LLRender::MM_MODELVIEW);
void LLTexLayerSetBuffer::popProjection() const
James Cook's avatar
James Cook committed
{
	gGL.matrixMode(LLRender::MM_PROJECTION);
James Cook's avatar
James Cook committed

	gGL.matrixMode(LLRender::MM_MODELVIEW);
// virtual
void LLTexLayerSetBuffer::preRenderTexLayerSet()
James Cook's avatar
James Cook committed
{
	// Set up an ortho projection
	pushProjection();
}

// virtual
void LLTexLayerSetBuffer::postRenderTexLayerSet(BOOL success)
James Cook's avatar
James Cook committed
{
	popProjection();
}

James Cook's avatar
James Cook committed
{
	// Default color mask for tex layer render
	gGL.setColorMask(true, true);

James Cook's avatar
James Cook committed
	BOOL success = TRUE;
	
	bool use_shaders = LLGLSLShader::sNoFixedFunction;

	if (use_shaders)
	{
		gAlphaMaskProgram.bind();
		gAlphaMaskProgram.setMinimumAlpha(0.004f);
	else
	{
		gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.00f);
	}
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed
	// Composite the color data
	LLGLSUIDefault gls_ui;
	success &= mTexLayerSet->render( getCompositeOriginX(), getCompositeOriginY(), 
									 getCompositeWidth(), getCompositeHeight() );
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed

	if (use_shaders)
	{
		gAlphaMaskProgram.unbind();
	}

James Cook's avatar
James Cook committed
	// reset GL state
	gGL.setColorMask(true, true);
	gGL.setSceneBlendType(LLRender::BT_ALPHA);
James Cook's avatar
James Cook committed

	return success;
}

//-----------------------------------------------------------------------------
James Cook's avatar
James Cook committed
// An ordered set of texture layers that get composited into a single texture.
//-----------------------------------------------------------------------------

LLTexLayerSetInfo::LLTexLayerSetInfo() :
James Cook's avatar
James Cook committed
	mBodyRegion( "" ),
	mWidth( 512 ),
	mHeight( 512 ),
	mClearAlpha( TRUE )
{
}

LLTexLayerSetInfo::~LLTexLayerSetInfo( )
{
	std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
}

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 ) )
	{
		llwarns << "<layer_set> is missing body_region attribute" << llendl;
		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 (layer_info_list_t::iterator layer_iter = mLayerInfoList.begin();
		 layer_iter != mLayerInfoList.end();
		 layer_iter++)
	{
		LLTexLayerInfo *layer_info = *layer_iter;
		layer_info->createVisualParams(appearance);
James Cook's avatar
James Cook committed
//-----------------------------------------------------------------------------
// 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 ),
	mBakedTexIndex(LLAvatarAppearanceDefines::BAKED_HEAD),
James Cook's avatar
James Cook committed
	mInfo( NULL )
{
}

James Cook's avatar
James Cook committed
LLTexLayerSet::~LLTexLayerSet()
{
James Cook's avatar
James Cook committed
	std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer());
	std::for_each(mMaskLayerList.begin(), mMaskLayerList.end(), DeletePointer());
James Cook's avatar
James Cook committed
}

//-----------------------------------------------------------------------------
// setInfo
//-----------------------------------------------------------------------------

BOOL LLTexLayerSet::setInfo(const LLTexLayerSetInfo *info)
James Cook's avatar
James Cook committed
{
	llassert(mInfo == NULL);
	mInfo = info;
	//mID = info->mID; // No ID

	mLayerList.reserve(info->mLayerInfoList.size());
	for (LLTexLayerSetInfo::layer_info_list_t::const_iterator iter = info->mLayerInfoList.begin(); 
		 iter != info->mLayerInfoList.end(); 
		 iter++)
James Cook's avatar
James Cook committed
	{
		LLTexLayerInterface *layer = NULL;
		if ( (*iter)->isUserSettable() )
		{
			layer = new LLTexLayerTemplate( this, getAvatarAppearance() );
		// this is the first time this layer (of either type) is being created - make sure you add the parameters to the avatar appearance
James Cook's avatar
James Cook committed
		{
			mInfo = NULL;
			return FALSE;
		}
		if (!layer->isVisibilityMask())
		{
		else
		{
			mMaskLayerList.push_back(layer);
		}
	}
James Cook's avatar
James Cook committed

	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( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
	{
		LLTexLayerInterface* layer = *iter;
James Cook's avatar
James Cook committed
		layer->deleteCaches();
	}
	for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++)
	{
		LLTexLayerInterface* layer = *iter;
James Cook's avatar
James Cook committed
}


BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height )
{
	BOOL success = TRUE;
James Cook's avatar
James Cook committed

	if (mMaskLayerList.size() > 0)
		for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++)
		{
			LLTexLayerInterface* layer = *iter;
			if (layer->isInvisibleAlphaMask())
			{
				mIsVisible = FALSE;
			}
		}
	bool use_shaders = LLGLSLShader::sNoFixedFunction;

	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();
		LLGLDisable no_alpha(GL_ALPHA_TEST);
			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);
James Cook's avatar
James Cook committed
	{
		// composite color layers
		for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
		{
			LLTexLayerInterface* layer = *iter;
			if (layer->getRenderPass() == LLTexLayer::RP_COLOR)
			{
				gGL.flush();
				success &= layer->render(x, y, width, height);
				gGL.flush();
			}
		}
		
		renderAlphaMaskTextures(x, y, width, height, false);
	
		stop_glerror();
	}
	else
	{
		gGL.flush();

		gGL.setSceneBlendType(LLRender::BT_REPLACE);
		LLGLDisable no_alpha(GL_ALPHA_TEST);
			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);
James Cook's avatar
James Cook committed

	return success;
}


BOOL LLTexLayerSet::isBodyRegion(const std::string& region) const 
{ 
	return mInfo->mBodyRegion == region; 
}

const std::string LLTexLayerSet::getBodyRegionName() const 
James Cook's avatar
James Cook committed

// virtual
void LLTexLayerSet::asLLSD(LLSD& sd) const
James Cook's avatar
James Cook committed
{
	sd["visible"] = LLSD::Boolean(isVisible());
	LLSD layer_list_sd;
	layer_list_t::const_iterator layer_iter = mLayerList.begin();
	layer_list_t::const_iterator layer_end  = mLayerList.end();
	for(; layer_iter != layer_end; ++layer_iter);
James Cook's avatar
James Cook committed
	{
		LLSD layer_sd;
		//LLTexLayerInterface* layer = (*layer_iter);
		//if (layer)
		//{
		//	layer->asLLSD(layer_sd);
		//}
		layer_list_sd.append(layer_sd);
James Cook's avatar
James Cook committed
	}
	LLSD mask_list_sd;
	LLSD info_sd;
	sd["layers"] = layer_list_sd;
	sd["masks"] = mask_list_sd;
	sd["info"] = info_sd;
James Cook's avatar
James Cook committed
}


void LLTexLayerSet::destroyComposite()
{
	if( mComposite )
	{
		mComposite = NULL;
	}
}

LLTexLayerSetBuffer* LLTexLayerSet::getComposite()
{
	if (!mComposite)
	{
		createComposite();
	}
	return mComposite;
}

const LLTexLayerSetBuffer* LLTexLayerSet::getComposite() const
{
James Cook's avatar
James Cook committed
	return mComposite;
}

static LLFastTimer::DeclareTimer FTM_GATHER_MORPH_MASK_ALPHA("gatherMorphMaskAlpha");
void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 origin_x, S32 origin_y, S32 width, S32 height)
James Cook's avatar
James Cook committed
{
	LLFastTimer t(FTM_GATHER_MORPH_MASK_ALPHA);
James Cook's avatar
James Cook committed
	memset(data, 255, width * height);

	for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
	{
		LLTexLayerInterface* layer = *iter;
		layer->gatherAlphaMasks(data, origin_x, origin_y, width, height);
James Cook's avatar
James Cook committed
	}
	
	// Set alpha back to that of our alpha masks.
	renderAlphaMaskTextures(origin_x, origin_y, width, height, true);
static LLFastTimer::DeclareTimer FTM_RENDER_ALPHA_MASK_TEXTURES("renderAlphaMaskTextures");
void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height, bool forceClear)
	LLFastTimer t(FTM_RENDER_ALPHA_MASK_TEXTURES);
	const LLTexLayerSetInfo *info = getInfo();
	
	bool use_shaders = LLGLSLShader::sNoFixedFunction;

	gGL.setColorMask(false, true);
	gGL.setSceneBlendType(LLRender::BT_REPLACE);
	// (Optionally) replace alpha with a single component image from a tga file.
			LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(info->mStaticAlphaFileName, TRUE);
			if( tex )
				gGL.getTexUnit(0)->bind(tex);
				gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE );
				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();
		LLGLDisable no_alpha(GL_ALPHA_TEST);
			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);
		gGL.getTexUnit(0)->setTextureBlendType( LLTexUnit::TB_REPLACE );
		for (layer_list_t::iterator iter = mMaskLayerList.begin(); iter != mMaskLayerList.end(); iter++)
		{
			LLTexLayerInterface* layer = *iter;
			layer->blendAlphaTexture(x,y,width, height);
			gGL.flush();
		}
		
	}
	
	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
	
	gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
	gGL.setColorMask(true, true);
	gGL.setSceneBlendType(LLRender::BT_ALPHA);
James Cook's avatar
James Cook committed
}

void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
	mAvatarAppearance->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex);
BOOL LLTexLayerSet::isMorphValid() const
	for(layer_list_t::const_iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
		const LLTexLayerInterface* layer = *iter;
		if (layer && !layer->isMorphValid())
		{
			return FALSE;
		}
	}
	return TRUE;
}

void LLTexLayerSet::invalidateMorphMasks()
{
	for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
	{
		LLTexLayerInterface* layer = *iter;
		if (layer)
		{
			layer->invalidateMorphMasks();
		}
	}
}

James Cook's avatar
James Cook committed
//-----------------------------------------------------------------------------
// LLTexLayerInfo
//-----------------------------------------------------------------------------
LLTexLayerInfo::LLTexLayerInfo() :
James Cook's avatar
James Cook committed
	mWriteAllChannels( FALSE ),
	mRenderPass(LLTexLayer::RP_COLOR),
James Cook's avatar
James Cook committed
	mFixedColor( 0.f, 0.f, 0.f, 0.f ),
	mLocalTexture( -1 ),
	mStaticImageIsMask( FALSE ),
	mUseLocalTextureAlphaOnly(FALSE),
	mIsVisibilityMask(FALSE)
James Cook's avatar
James Cook committed
{
}

LLTexLayerInfo::~LLTexLayerInfo( )
{
	std::for_each(mParamColorInfoList.begin(), mParamColorInfoList.end(), DeletePointer());
	std::for_each(mParamAlphaInfoList.begin(), mParamAlphaInfoList.end(), DeletePointer());
James Cook's avatar
James Cook committed
}

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;
James Cook's avatar
James Cook committed
	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;
James Cook's avatar
James Cook committed
		}
	}

	// 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;
	}

James Cook's avatar
James Cook committed
	// 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;
James Cook's avatar
James Cook committed
		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))
James Cook's avatar
James Cook committed
		{
			texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly );

			/* if ("upper_shirt" == local_texture_name)
				mLocalTexture = TEX_UPPER_SHIRT; */
			mLocalTexture = TEX_NUM_INDICES;
			for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin();
				 iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end();
James Cook's avatar
James Cook committed
			{
				const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
				if (local_texture_name == texture_dict->mName)
James Cook's avatar
James Cook committed
			{
					mLocalTexture = iter->first;
					break;
James Cook's avatar
James Cook committed
			}
			}
			if (mLocalTexture == TEX_NUM_INDICES)
James Cook's avatar
James Cook committed
			{
				llwarns << "<texture> element has invalid local_texture attribute: " << mName << " " << local_texture_name << llendl;
James Cook's avatar
James Cook committed
				return FALSE;
			}
		}
		else	
		{
			llwarns << "<texture> element is missing a required attribute. " << mName << llendl;
			return FALSE;
		}
	}

	for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" );
		 maskNode;
		 maskNode = node->getNextNamedChild())
	{
James Cook's avatar
James Cook committed
		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));
James Cook's avatar
James Cook committed
		}
	}

	// <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();
James Cook's avatar
James Cook committed
			if (!info->parseXml(child))
			{
				delete info;
				return FALSE;
			}
			mParamColorInfoList.push_back(info);
James Cook's avatar
James Cook committed
		}
		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);
BOOL LLTexLayerInfo::createVisualParams(LLAvatarAppearance *appearance)
{
	BOOL success = TRUE;
	for (param_color_info_list_t::iterator color_info_iter = mParamColorInfoList.begin();
		 color_info_iter != mParamColorInfoList.end();
		 color_info_iter++)
	{
		LLTexLayerParamColorInfo * color_info = *color_info_iter;
		LLTexLayerParamColor* param_color = new LLTexLayerParamColor(appearance);
		if (!param_color->setInfo(color_info, TRUE))
		{
			llwarns << "NULL TexLayer Color Param could not be added to visual param list. Deleting." << llendl;
			delete param_color;
			success = FALSE;
		}
	}

	for (param_alpha_info_list_t::iterator alpha_info_iter = mParamAlphaInfoList.begin();
		 alpha_info_iter != mParamAlphaInfoList.end();
		 alpha_info_iter++)
	{
		LLTexLayerParamAlphaInfo * alpha_info = *alpha_info_iter;
		LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha(appearance);
		if (!param_alpha->setInfo(alpha_info, TRUE))
		{
			llwarns << "NULL TexLayer Alpha Param could not be added to visual param list. Deleting." << llendl;
			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) 
	{
			llwarns << "mInfo != NULL" << llendl;
	}
	mParamColorList.reserve(mInfo->mParamColorInfoList.size());
	for (param_color_info_list_t::const_iterator iter = mInfo->mParamColorInfoList.begin(); 
		 iter != mInfo->mParamColorInfoList.end(); 
		 iter++)
	{
		LLTexLayerParamColor* param_color;
			{
				param_color = new LLTexLayerParamColor(this);
				if (!param_color->setInfo(*iter, TRUE))
				{
					mInfo = NULL;
					return FALSE;
				}
			}
			else
			{
				param_color = (LLTexLayerParamColor*)wearable->getVisualParam((*iter)->getID());
				if (!param_color)
				{
					mInfo = NULL;
					return FALSE;
				}
			}
			mParamColorList.push_back( param_color );
		}

	mParamAlphaList.reserve(mInfo->mParamAlphaInfoList.size());
	for (param_alpha_info_list_t::const_iterator iter = mInfo->mParamAlphaInfoList.begin(); 
		 iter != mInfo->mParamAlphaInfoList.end(); 
		 iter++)
		{
			LLTexLayerParamAlpha* param_alpha;
			if (!wearable)
			{
				param_alpha = new LLTexLayerParamAlpha( this );
				if (!param_alpha->setInfo(*iter, TRUE))
				{
					mInfo = NULL;
					return FALSE;
				}
			}
			else
			{
				param_alpha = (LLTexLayerParamAlpha*) wearable->getVisualParam((*iter)->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;
		param_color_list_t::const_iterator color_iter = mParamColorList.begin();
		param_alpha_list_t::const_iterator alpha_iter = mParamAlphaList.begin();

		for (; color_iter != mParamColorList.end(); color_iter++)
		{
			LLTexLayerParamColor* param = *color_iter;
			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 (; alpha_iter != mParamAlphaList.end(); alpha_iter++)
		{
			LLTexLayerParamAlpha* param = *alpha_iter;
			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 LLAvatarAppearanceDictionary::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