Skip to content
Snippets Groups Projects
llavatarappearance.cpp 65 KiB
Newer Older
/** 
 * @File llavatarappearance.cpp
 * @brief Implementation of LLAvatarAppearance class
 *
 * $LicenseInfo:firstyear=2012&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$
 */

#if LL_MSVC
// disable warning about boost::lexical_cast returning uninitialized data
// when it fails to parse the string
#pragma warning (disable:4701)
#endif

#include "llavatarappearance.h"
#include "lldir.h"
#include "llpolymorph.h"
#include "llpolymesh.h"
#include "llpolyskeletaldistortion.h"
#include "lltexglobalcolor.h"
#include "llwearabledata.h"

#if LL_MSVC
// disable boost::lexical_cast warning
#pragma warning (disable:4702)
#endif

#include <boost/lexical_cast.hpp>

using namespace LLAvatarAppearanceDefines;

//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------

const std::string AVATAR_DEFAULT_CHAR = "avatar";
const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0);

/*********************************************************************************
 **                                                                             **
 ** Begin private LLAvatarAppearance Support classes
 **
 **/

//------------------------------------------------------------------------
// LLAvatarBoneInfo
// Trans/Scale/Rot etc. info about each avatar bone.  Used by LLVOAvatarSkeleton.
//------------------------------------------------------------------------
class LLAvatarBoneInfo
{
	friend class LLAvatarAppearance;
	friend class LLAvatarSkeletonInfo;
public:
	LLAvatarBoneInfo() : mIsJoint(FALSE) {}
	~LLAvatarBoneInfo()
	{
		std::for_each(mChildren.begin(), mChildren.end(), DeletePointer());
		mChildren.clear();
	}
	BOOL parseXml(LLXmlTreeNode* node);
	
private:
	std::string mName;
	LLVector3 mRot;
	LLVector3 mScale;
	LLVector3 mPivot;
	typedef std::vector<LLAvatarBoneInfo*> bones_t;
	bones_t mChildren;
};

//------------------------------------------------------------------------
// LLAvatarSkeletonInfo
// Overall avatar skeleton
//------------------------------------------------------------------------
class LLAvatarSkeletonInfo
{
	friend class LLAvatarAppearance;
public:
	LLAvatarSkeletonInfo() :
		mNumBones(0), mNumCollisionVolumes(0) {}
	~LLAvatarSkeletonInfo()
	{
		std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer());
		mBoneInfoList.clear();
	}
	BOOL parseXml(LLXmlTreeNode* node);
	S32 getNumBones() const { return mNumBones; }
	S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
	
private:
	S32 mNumBones;
	S32 mNumCollisionVolumes;
    LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
	typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
	bone_info_list_t mBoneInfoList;
};

//-----------------------------------------------------------------------------
// LLAvatarXmlInfo
//-----------------------------------------------------------------------------

LLAvatarAppearance::LLAvatarXmlInfo::LLAvatarXmlInfo()
	: mTexSkinColorInfo(0), mTexHairColorInfo(0), mTexEyeColorInfo(0)
{
}

LLAvatarAppearance::LLAvatarXmlInfo::~LLAvatarXmlInfo()
{
	std::for_each(mMeshInfoList.begin(), mMeshInfoList.end(), DeletePointer());
	std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());		
	mSkeletalDistortionInfoList.clear();

	std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
	mAttachmentInfoList.clear();

	delete_and_clear(mTexSkinColorInfo);
	delete_and_clear(mTexHairColorInfo);
	delete_and_clear(mTexEyeColorInfo);
	std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());		
	std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
	std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer());
	mMorphMaskInfoList.clear();
}


/**
 **
 ** End LLAvatarAppearance Support classes
 **                                                                             **
 *********************************************************************************/

//-----------------------------------------------------------------------------
// Static Data
//-----------------------------------------------------------------------------
LLAvatarSkeletonInfo* LLAvatarAppearance::sAvatarSkeletonInfo = NULL;
LLAvatarAppearance::LLAvatarXmlInfo* LLAvatarAppearance::sAvatarXmlInfo = NULL;
LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary* LLAvatarAppearance::sAvatarDictionary = NULL;
LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
	mTexSkinColor( NULL ),
	mTexHairColor( NULL ),
	mTexEyeColor( NULL ),
    mNumBones(0),
    mNumCollisionVolumes(0),
    mCollisionVolumes(NULL),
	mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES);
	for (U32 i = 0; i < mBakedTextureDatas.size(); i++ )
	{
		mBakedTextureDatas[i].mLastTextureID = IMG_DEFAULT_AVATAR;
		mBakedTextureDatas[i].mTexLayerSet = NULL;
		mBakedTextureDatas[i].mIsLoaded = false;
		mBakedTextureDatas[i].mIsUsed = false;
		mBakedTextureDatas[i].mMaskTexName = 0;
		mBakedTextureDatas[i].mTextureIndex = sAvatarDictionary->bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i);
}

// virtual
void LLAvatarAppearance::initInstance()
{
	//-------------------------------------------------------------------------
	// initialize joint, mesh and shape members
	//-------------------------------------------------------------------------
	mRoot = createAvatarJoint();
	mRoot->setName( "mRoot" );

	for (const auto& mesh_entry_pair : sAvatarDictionary->getMeshEntries())
Rye Mutt's avatar
Rye Mutt committed
		const EMeshIndex mesh_index = mesh_entry_pair.first;
		const LLAvatarAppearanceDictionary::MeshEntry *mesh_dict = mesh_entry_pair.second;
		LLAvatarJoint* joint = createAvatarJoint();
		joint->setName(mesh_dict->mName);
		joint->setMeshID(mesh_index);
		mMeshLOD.push_back(joint);
		
		/* mHairLOD.setName("mHairLOD");
		   mHairMesh0.setName("mHairMesh0");
		   mHairMesh0.setMeshID(MESH_ID_HAIR);
		   mHairMesh1.setName("mHairMesh1"); */
		for (U32 lod = 0; lod < mesh_dict->mLOD; lod++)
		{
			LLAvatarJointMesh* mesh = createAvatarJointMesh();
			std::string mesh_name = "m" + mesh_dict->mName + boost::lexical_cast<std::string>(lod);
			// We pre-pended an m - need to capitalize first character for camelCase
			mesh_name[1] = toupper(mesh_name[1]);
			mesh->setName(mesh_name);
			mesh->setMeshID(mesh_index);
			mesh->setPickName(mesh_dict->mPickName);
			mesh->setIsTransparent(FALSE);
			{
				case MESH_ID_HAIR:
					mesh->setIsTransparent(TRUE);
					break;
				case MESH_ID_SKIRT:
					mesh->setIsTransparent(TRUE);
					break;
				case MESH_ID_EYEBALL_LEFT:
				case MESH_ID_EYEBALL_RIGHT:
					mesh->setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
					break;
			}
			
			joint->mMeshParts.push_back(mesh);
		}
	}

	//-------------------------------------------------------------------------
	// associate baked textures with meshes
	//-------------------------------------------------------------------------
	for (const auto& mesh_entry_pair : sAvatarDictionary->getMeshEntries())
Rye Mutt's avatar
Rye Mutt committed
		const EMeshIndex mesh_index = mesh_entry_pair.first;
		const LLAvatarAppearanceDictionary::MeshEntry *mesh_dict = mesh_entry_pair.second;
		const EBakedTextureIndex baked_texture_index = mesh_dict->mBakedID;
		// Skip it if there's no associated baked texture.
		if (baked_texture_index == BAKED_NUM_INDICES) continue;
		
Rye Mutt's avatar
Rye Mutt committed
		for (LLAvatarJointMesh* mesh : mMeshLOD[mesh_index]->mMeshParts)
			mBakedTextureDatas[(S32)baked_texture_index].mJointMeshes.push_back(mesh);
	delete_and_clear(mTexSkinColor);
	delete_and_clear(mTexHairColor);
	delete_and_clear(mTexEyeColor);

	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
	{
		delete_and_clear(mBakedTextureDatas[i].mTexLayerSet);
		mBakedTextureDatas[i].mJointMeshes.clear();

		for (morph_list_t::iterator iter2 = mBakedTextureDatas[i].mMaskedMorphs.begin();
			 iter2 != mBakedTextureDatas[i].mMaskedMorphs.end(); iter2++)
		{
			LLMaskedMorph* masked_morph = (*iter2);
			delete masked_morph;
		}
	}

Rye Mutt's avatar
Rye Mutt committed
	if (mRoot)
    {
        mRoot->removeAllChildren();
        delete mRoot;
    }
	delete_and_clear_array(mCollisionVolumes);
	std::for_each(mPolyMeshes.begin(), mPolyMeshes.end(), DeletePairedPointer());
	mPolyMeshes.clear();
	for (avatar_joint_list_t::iterator jointIter = mMeshLOD.begin();
		LLAvatarJoint* joint = *jointIter;
		std::for_each(joint->mMeshParts.begin(), joint->mMeshParts.end(), DeletePointer());
		joint->mMeshParts.clear();
	}
	std::for_each(mMeshLOD.begin(), mMeshLOD.end(), DeletePointer());
	mMeshLOD.clear();
}

//static
void LLAvatarAppearance::initClass()
{
//static
void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, const std::string& skeleton_file_name_arg)
{
    // init dictionary (don't repeat on second login attempt)
    if (!sAvatarDictionary)
    {
        sAvatarDictionary = new LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary();
    }

	std::string avatar_file_name;

    if (!avatar_file_name_arg.empty())
    {
        avatar_file_name = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,avatar_file_name_arg);
    }
    else
    {
        avatar_file_name = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR + "_lad.xml");
    }
	LLXmlTree xml_tree;
	BOOL success = xml_tree.parseFile( avatar_file_name, FALSE );
		LL_ERRS() << "Problem reading avatar configuration file:" << avatar_file_name << LL_ENDL;
	LLXmlTreeNode* root = xml_tree.getRoot();
		LL_ERRS() << "No root node found in avatar configuration file: " << avatar_file_name << LL_ENDL;
		return;
	}

	//-------------------------------------------------------------------------
	//-------------------------------------------------------------------------
	if( !root->hasName( "linden_avatar" ) )
	{
		LL_ERRS() << "Invalid avatar file header: " << avatar_file_name << LL_ENDL;
	}
	
	std::string version;
	static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
	if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
		LL_ERRS() << "Invalid avatar file version: " << version << " in file: " << avatar_file_name << LL_ENDL;
	}

	S32 wearable_def_version = 1;
	static LLStdStringHandle wearable_definition_version_string = LLXmlTree::addAttributeString("wearable_definition_version");
	root->getFastAttributeS32( wearable_definition_version_string, wearable_def_version );
	LLWearable::setCurrentDefinitionVersion( wearable_def_version );

	LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
	if (!skeleton_node)
	{
		LL_ERRS() << "No skeleton in avatar configuration file: " << avatar_file_name << LL_ENDL;

    std::string skeleton_file_name = skeleton_file_name_arg;
    if (skeleton_file_name.empty())
    {
        static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
        if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
        {
            LL_ERRS() << "No file name in skeleton node in avatar config file: " << avatar_file_name << LL_ENDL;
        }
    }
	LLXmlTree skeleton_xml_tree;
	skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
	if (!parseSkeletonFile(skeleton_path, skeleton_xml_tree))
		LL_ERRS() << "Error parsing skeleton file: " << skeleton_path << LL_ENDL;
	}

	// Process XML data

	// avatar_skeleton.xml
	if (sAvatarSkeletonInfo)
	{ //this can happen if a login attempt failed
		delete sAvatarSkeletonInfo;
	}
	sAvatarSkeletonInfo = new LLAvatarSkeletonInfo;
	if (!sAvatarSkeletonInfo->parseXml(skeleton_xml_tree.getRoot()))
		LL_ERRS() << "Error parsing skeleton XML file: " << skeleton_path << LL_ENDL;
	}
	// parse avatar_lad.xml
	if (sAvatarXmlInfo)
	{ //this can happen if a login attempt failed
	}
	sAvatarXmlInfo = new LLAvatarXmlInfo;
	if (!sAvatarXmlInfo->parseXmlSkeletonNode(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
	}
	if (!sAvatarXmlInfo->parseXmlMeshNodes(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
	}
	if (!sAvatarXmlInfo->parseXmlColorNodes(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
	}
	if (!sAvatarXmlInfo->parseXmlLayerNodes(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
	}
	if (!sAvatarXmlInfo->parseXmlDriverNodes(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
	}
	if (!sAvatarXmlInfo->parseXmlMorphNodes(root))
	{
		LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
void LLAvatarAppearance::cleanupClass()
{
    delete_and_clear(sAvatarDictionary);
    delete_and_clear(sAvatarSkeletonInfo);
void LLAvatarAppearance::compareJointStateMaps(joint_state_map_t& last_state,
                                               joint_state_map_t& curr_state)
{
    if (!last_state.empty() && (last_state != curr_state))
    {
        S32 diff_count = 0;
        joint_state_map_t::iterator it;
        for (it=last_state.begin(); it != last_state.end(); ++it)
        {
            const std::string& key = it->first;
            if (last_state[key] != curr_state[key])
            {
                LL_DEBUGS("AvatarBodySize") << "BodySize change " << key << " " << last_state[key] << "->" << curr_state[key] << LL_ENDL;
                diff_count++;
            }
        }
        if (diff_count > 0)
        {
            LL_DEBUGS("AvatarBodySize") << "Total of BodySize changes " << diff_count << LL_ENDL;
        }
        
    }
}

//------------------------------------------------------------------------
// The viewer can only suggest a good size for the agent,
// the simulator will keep it inside a reasonable range.
void LLAvatarAppearance::computeBodySize() 
{
    mLastBodySizeState = mCurrBodySizeState;

    mCurrBodySizeState["mPelvis scale"] = mPelvisp->getScale();
    mCurrBodySizeState["mSkull pos"] = mSkullp->getPosition();
    mCurrBodySizeState["mSkull scale"] = mSkullp->getScale();
    mCurrBodySizeState["mNeck pos"] = mNeckp->getPosition();
    mCurrBodySizeState["mNeck scale"] = mNeckp->getScale();
    mCurrBodySizeState["mChest pos"] = mChestp->getPosition();
    mCurrBodySizeState["mChest scale"] = mChestp->getScale();
    mCurrBodySizeState["mHead pos"] = mHeadp->getPosition();
    mCurrBodySizeState["mHead scale"] = mHeadp->getScale();
    mCurrBodySizeState["mTorso pos"] = mTorsop->getPosition();
    mCurrBodySizeState["mTorso scale"] = mTorsop->getScale();
    mCurrBodySizeState["mHipLeft pos"] = mHipLeftp->getPosition();
    mCurrBodySizeState["mHipLeft scale"] = mHipLeftp->getScale();
    mCurrBodySizeState["mKneeLeft pos"] = mKneeLeftp->getPosition();
    mCurrBodySizeState["mKneeLeft scale"] = mKneeLeftp->getScale();
    mCurrBodySizeState["mAnkleLeft pos"] = mAnkleLeftp->getPosition();
    mCurrBodySizeState["mAnkleLeft scale"] = mAnkleLeftp->getScale();
    mCurrBodySizeState["mFootLeft pos"] = mFootLeftp->getPosition();

	LLVector3 pelvis_scale = mPelvisp->getScale();

	// some of the joints have not been cached
	LLVector3 skull = mSkullp->getPosition();
Don Kjer's avatar
Don Kjer committed
	//LLVector3 skull_scale = mSkullp->getScale();

	LLVector3 neck = mNeckp->getPosition();
	LLVector3 neck_scale = mNeckp->getScale();

	LLVector3 chest = mChestp->getPosition();
	LLVector3 chest_scale = mChestp->getScale();

	// the rest of the joints have been cached
	LLVector3 head = mHeadp->getPosition();
	LLVector3 head_scale = mHeadp->getScale();

	LLVector3 torso = mTorsop->getPosition();
	LLVector3 torso_scale = mTorsop->getScale();

	LLVector3 hip = mHipLeftp->getPosition();
	LLVector3 hip_scale = mHipLeftp->getScale();

	LLVector3 knee = mKneeLeftp->getPosition();
	LLVector3 knee_scale = mKneeLeftp->getScale();

	LLVector3 ankle = mAnkleLeftp->getPosition();
	LLVector3 ankle_scale = mAnkleLeftp->getScale();

	LLVector3 foot  = mFootLeftp->getPosition();

	F32 old_offset = mAvatarOffset.mV[VZ];

	mAvatarOffset.mV[VZ] = getVisualParamWeight(AVATAR_HOVER);
	mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
				 	knee.mV[VZ] * hip_scale.mV[VZ] -
				 	ankle.mV[VZ] * knee_scale.mV[VZ] -
				 	foot.mV[VZ] * ankle_scale.mV[VZ];

	LLVector3 new_body_size;
	new_body_size.mV[VZ] = mPelvisToFoot +
					   // the sqrt(2) correction below is an approximate
					   // correction to get to the top of the head
					   F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) + 
					   head.mV[VZ] * neck_scale.mV[VZ] + 
					   neck.mV[VZ] * chest_scale.mV[VZ] + 
					   chest.mV[VZ] * torso_scale.mV[VZ] + 
					   torso.mV[VZ] * pelvis_scale.mV[VZ]; 

	// TODO -- measure the real depth and width
	new_body_size.mV[VX] = DEFAULT_AGENT_DEPTH;
	new_body_size.mV[VY] = DEFAULT_AGENT_WIDTH;

	mAvatarOffset.mV[VX] = 0.0f;
	mAvatarOffset.mV[VY] = 0.0f;

	if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ])
        compareJointStateMaps(mLastBodySizeState, mCurrBodySizeState);
	}
}

//-----------------------------------------------------------------------------
// parseSkeletonFile()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree)
{
	//-------------------------------------------------------------------------
	// parse the file
	//-------------------------------------------------------------------------
	BOOL parsesuccess = skeleton_xml_tree.parseFile( filename, FALSE );
		LL_ERRS() << "Can't parse skeleton file: " << filename << LL_ENDL;
	LLXmlTreeNode* root = skeleton_xml_tree.getRoot();
		LL_ERRS() << "No root node found in avatar skeleton file: " << filename << LL_ENDL;
		return FALSE;
	}

	if( !root->hasName( "linden_skeleton" ) )
	{
		LL_ERRS() << "Invalid avatar skeleton file header: " << filename << LL_ENDL;
		return FALSE;
	}

	std::string version;
	static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
	if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
		LL_ERRS() << "Invalid avatar skeleton file version: " << version << " in file: " << filename << LL_ENDL;
		return FALSE;
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
// setupBone()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 &volume_num, S32 &joint_num)
{
	LLJoint* joint = NULL;

    LL_DEBUGS("BVH") << "bone info: name " << info->mName
                     << " isJoint " << info->mIsJoint
                     << " volume_num " << volume_num
                     << " joint_num " << joint_num
                     << LL_ENDL;
	if (info->mIsJoint)
	{
		joint = getCharacterJoint(joint_num);
		if (!joint)
		{
			LL_WARNS() << "Too many bones" << LL_ENDL;
			return FALSE;
		}
		joint->setName( info->mName );
	}
	else // collision volume
	{
		if (volume_num >= (S32)mNumCollisionVolumes)
		{
			LL_WARNS() << "Too many collision volumes" << LL_ENDL;
			return FALSE;
		}
		joint = (&mCollisionVolumes[volume_num]);
		joint->setName( info->mName );
	}

	// add to parent
    joint->setDefaultPosition(info->mPos);
	joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
							 info->mRot.mV[VZ], LLQuaternion::XYZ));
	joint->setScale(info->mScale);

	if (info->mIsJoint)
	{
		joint->setSkinOffset( info->mPivot );
	LLAvatarBoneInfo::bones_t::const_iterator iter;
	for (iter = info->mChildren.begin(); iter != info->mChildren.end(); ++iter)
	{
		LLAvatarBoneInfo *child_info = *iter;
		if (!setupBone(child_info, joint, volume_num, joint_num))
		{
			return FALSE;
		}
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
// allocateCharacterJoints()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::allocateCharacterJoints( U32 num )
{
    if (mSkeleton.size() != num)
    {
        clearSkeleton();
        mSkeleton = avatar_joint_list_t(num,NULL);
//-----------------------------------------------------------------------------
// buildSkeleton()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
{
    LL_DEBUGS("BVH") << "numBones " << info->mNumBones << " numCollisionVolumes " << info->mNumCollisionVolumes << LL_ENDL;
	// allocate joints
	if (!allocateCharacterJoints(info->mNumBones))
	{
		LL_ERRS() << "Can't allocate " << info->mNumBones << " joints" << LL_ENDL;
		return FALSE;
	}
	
	// allocate volumes
	if (info->mNumCollisionVolumes)
	{
		if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
		{
			LL_ERRS() << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << LL_ENDL;
			return FALSE;
		}
	}

	S32 current_joint_num = 0;
	S32 current_volume_num = 0;
	LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter;
	for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter)
	{
		LLAvatarBoneInfo *bone_info = *iter;
		if (!setupBone(bone_info, NULL, current_volume_num, current_joint_num))
			LL_ERRS() << "Error parsing bone in skeleton file" << LL_ENDL;
//-----------------------------------------------------------------------------
// clearSkeleton()
//-----------------------------------------------------------------------------
void LLAvatarAppearance::clearSkeleton()
{
	std::for_each(mSkeleton.begin(), mSkeleton.end(), DeletePointer());
	mSkeleton.clear();
}

//------------------------------------------------------------------------
// addPelvisFixup
//------------------------------------------------------------------------
void LLAvatarAppearance::addPelvisFixup( F32 fixup, const LLUUID& mesh_id ) 
{
	LLVector3 pos(0.0,0.0,fixup);
	mPelvisFixups.add(mesh_id,pos);
}

//------------------------------------------------------------------------
// addPelvisFixup
//------------------------------------------------------------------------
void LLAvatarAppearance::removePelvisFixup( const LLUUID& mesh_id )
{
	mPelvisFixups.remove(mesh_id);
}

//------------------------------------------------------------------------
// hasPelvisFixup
//------------------------------------------------------------------------
bool LLAvatarAppearance::hasPelvisFixup( F32& fixup, LLUUID& mesh_id ) const
{
	LLVector3 pos;
	if (mPelvisFixups.findActiveOverride(mesh_id,pos))
	{
		fixup = pos[2];
		return true;
	}
	return false;
}

bool LLAvatarAppearance::hasPelvisFixup( F32& fixup ) const
{
	LLUUID mesh_id;
	return hasPelvisFixup( fixup, mesh_id );
}
//-----------------------------------------------------------------------------
// LLAvatarAppearance::buildCharacter()
// Deferred initialization and rebuild of the avatar.
//-----------------------------------------------------------------------------
void LLAvatarAppearance::buildCharacter()
{
	//-------------------------------------------------------------------------
	// remove all references to our existing skeleton
	// so we can rebuild it
	//-------------------------------------------------------------------------
	flushAllMotions();

	//-------------------------------------------------------------------------
	// remove all of mRoot's children
	//-------------------------------------------------------------------------
	mRoot->removeAllChildren();
	mJointMap.clear();
	mIsBuilt = FALSE;

	//-------------------------------------------------------------------------
	// clear mesh data
	//-------------------------------------------------------------------------
	for (avatar_joint_list_t::iterator jointIter = mMeshLOD.begin();
		 jointIter != mMeshLOD.end(); ++jointIter)
	{
		LLAvatarJoint* joint = *jointIter;
		for (avatar_joint_mesh_list_t::iterator meshIter = joint->mMeshParts.begin();
			 meshIter != joint->mMeshParts.end(); ++meshIter)
		{
			LLAvatarJointMesh * mesh = *meshIter;
			mesh->setMesh(NULL);
		}
	}

	//-------------------------------------------------------------------------
	// (re)load our skeleton and meshes
	//-------------------------------------------------------------------------
	LLTimer timer;

	BOOL status = loadAvatar();
	stop_glerror();

// 	gPrintMessagesThisFrame = TRUE;
	LL_DEBUGS() << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << LL_ENDL;
			LL_ERRS() << "Unable to load user's avatar" << LL_ENDL;
			LL_WARNS() << "Unable to load other's avatar" << LL_ENDL;
		}
		return;
	}

	//-------------------------------------------------------------------------
	// initialize "well known" joint pointers
	//-------------------------------------------------------------------------
	mPelvisp		= mRoot->findJoint("mPelvis");
	mTorsop			= mRoot->findJoint("mTorso");
	mChestp			= mRoot->findJoint("mChest");
	mNeckp			= mRoot->findJoint("mNeck");
	mHeadp			= mRoot->findJoint("mHead");
	mSkullp			= mRoot->findJoint("mSkull");
	mHipLeftp		= mRoot->findJoint("mHipLeft");
	mHipRightp		= mRoot->findJoint("mHipRight");
	mKneeLeftp		= mRoot->findJoint("mKneeLeft");
	mKneeRightp		= mRoot->findJoint("mKneeRight");
	mAnkleLeftp		= mRoot->findJoint("mAnkleLeft");
	mAnkleRightp	= mRoot->findJoint("mAnkleRight");
	mFootLeftp		= mRoot->findJoint("mFootLeft");
	mFootRightp		= mRoot->findJoint("mFootRight");
	mWristLeftp		= mRoot->findJoint("mWristLeft");
	mWristRightp	= mRoot->findJoint("mWristRight");
	mEyeLeftp		= mRoot->findJoint("mEyeLeft");
	mEyeRightp		= mRoot->findJoint("mEyeRight");

	//-------------------------------------------------------------------------
	// Make sure "well known" pointers exist
	//-------------------------------------------------------------------------
	if (!(mPelvisp && 
		  mTorsop &&
		  mChestp &&
		  mNeckp &&
		  mHeadp &&
		  mSkullp &&
		  mHipLeftp &&
		  mHipRightp &&
		  mKneeLeftp &&
		  mKneeRightp &&
		  mAnkleLeftp &&
		  mAnkleRightp &&
		  mFootLeftp &&
		  mFootRightp &&
		  mWristLeftp &&
		  mWristRightp &&
		  mEyeLeftp &&
		  mEyeRightp))
	{
		LL_ERRS() << "Failed to create avatar." << LL_ENDL;
		return;
	}

	//-------------------------------------------------------------------------
	// initialize the pelvis
	//-------------------------------------------------------------------------
	mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );

	mIsBuilt = TRUE;
	stop_glerror();

}

BOOL LLAvatarAppearance::loadAvatar()
{
// 	LL_RECORD_BLOCK_TIME(FTM_LOAD_AVATAR);
	
	// avatar_skeleton.xml
	if( !buildSkeleton(sAvatarSkeletonInfo) )
	{
		LL_ERRS() << "avatar file: buildSkeleton() failed" << LL_ENDL;
		return FALSE;
	}

	// avatar_lad.xml : <skeleton>
	if( !loadSkeletonNode() )
	{
		LL_ERRS() << "avatar file: loadNodeSkeleton() failed" << LL_ENDL;
		return FALSE;
	}
	
	// avatar_lad.xml : <mesh>
	if( !loadMeshNodes() )
	{
		LL_ERRS() << "avatar file: loadNodeMesh() failed" << LL_ENDL;
		return FALSE;
	}
	
	// avatar_lad.xml : <global_color>
	if( sAvatarXmlInfo->mTexSkinColorInfo )
	{
		mTexSkinColor = new LLTexGlobalColor( this );
		if( !mTexSkinColor->setInfo( sAvatarXmlInfo->mTexSkinColorInfo ) )
		{
			LL_ERRS() << "avatar file: mTexSkinColor->setInfo() failed" << LL_ENDL;
		LL_ERRS() << "<global_color> name=\"skin_color\" not found" << LL_ENDL;
		return FALSE;
	}
	if( sAvatarXmlInfo->mTexHairColorInfo )
	{
		mTexHairColor = new LLTexGlobalColor( this );
		if( !mTexHairColor->setInfo( sAvatarXmlInfo->mTexHairColorInfo ) )
		{
			LL_ERRS() << "avatar file: mTexHairColor->setInfo() failed" << LL_ENDL;
		LL_ERRS() << "<global_color> name=\"hair_color\" not found" << LL_ENDL;
		return FALSE;
	}
	if( sAvatarXmlInfo->mTexEyeColorInfo )
	{
		mTexEyeColor = new LLTexGlobalColor( this );
		if( !mTexEyeColor->setInfo( sAvatarXmlInfo->mTexEyeColorInfo ) )
		{
			LL_ERRS() << "avatar file: mTexEyeColor->setInfo() failed" << LL_ENDL;
		LL_ERRS() << "<global_color> name=\"eye_color\" not found" << LL_ENDL;
		return FALSE;
	}
	
	// avatar_lad.xml : <layer_set>
	if (sAvatarXmlInfo->mLayerInfoList.empty())
	{
		LL_ERRS() << "avatar file: missing <layer_set> node" << LL_ENDL;
		return FALSE;
	}

	if (sAvatarXmlInfo->mMorphMaskInfoList.empty())
	{
		LL_ERRS() << "avatar file: missing <morph_masks> node" << LL_ENDL;