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$
*/
#include "linden_common.h"
Nyx (Neal Orman)
committed
#include "llavatarappearance.h"
Nyx (Neal Orman)
committed
#include "llavatarappearancedefines.h"
Nyx (Neal Orman)
committed
#include "llavatarjointmesh.h"
Richard Linden
committed
#include "llstl.h"
Nyx (Neal Orman)
committed
#include "lldir.h"
#include "llpolymorph.h"
#include "llpolymesh.h"
#include "llpolyskeletaldistortion.h"
#include "llstl.h"
#include "lltexglobalcolor.h"
#include "llwearabledata.h"
Richard Linden
committed
#include "boost/bind.hpp"
Aura Linden
committed
#include "boost/tokenizer.hpp"
#include <boost/lexical_cast.hpp>
using namespace LLAvatarAppearanceDefines;
Nyx (Neal Orman)
committed
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
const std::string AVATAR_DEFAULT_CHAR = "avatar";
const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0);
Nyx (Neal Orman)
committed
/*********************************************************************************
** **
** 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()
{
Graham Linden
committed
std::for_each(mChildren.begin(), mChildren.end(), DeletePointer());
mChildren.clear();
Nyx (Neal Orman)
committed
}
BOOL parseXml(LLXmlTreeNode* node);
private:
std::string mName;
Brad Payne (Vir Linden)
committed
std::string mSupport;
Aura Linden
committed
std::string mAliases;
Nyx (Neal Orman)
committed
BOOL mIsJoint;
LLVector3 mPos;
Brad Payne (Vir Linden)
committed
LLVector3 mEnd;
Nyx (Neal Orman)
committed
LLVector3 mRot;
LLVector3 mScale;
LLVector3 mPivot;
Graham Linden
committed
typedef std::vector<LLAvatarBoneInfo*> bones_t;
bones_t mChildren;
Nyx (Neal Orman)
committed
};
//------------------------------------------------------------------------
// 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();
Nyx (Neal Orman)
committed
}
BOOL parseXml(LLXmlTreeNode* node);
S32 getNumBones() const { return mNumBones; }
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
private:
S32 mNumBones;
S32 mNumCollisionVolumes;
Aura Linden
committed
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
Nyx (Neal Orman)
committed
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());
mMeshInfoList.clear();
Nyx (Neal Orman)
committed
std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());
mSkeletalDistortionInfoList.clear();
Nyx (Neal Orman)
committed
std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
mAttachmentInfoList.clear();
Richard Linden
committed
delete_and_clear(mTexSkinColorInfo);
delete_and_clear(mTexHairColorInfo);
delete_and_clear(mTexEyeColorInfo);
Nyx (Neal Orman)
committed
std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
mLayerInfoList.clear();
Nyx (Neal Orman)
committed
std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
mDriverInfoList.clear();
Nyx (Neal Orman)
committed
std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer());
mMorphMaskInfoList.clear();
Nyx (Neal Orman)
committed
}
/**
**
** End LLAvatarAppearance Support classes
** **
*********************************************************************************/
//-----------------------------------------------------------------------------
// Static Data
//-----------------------------------------------------------------------------
LLAvatarSkeletonInfo* LLAvatarAppearance::sAvatarSkeletonInfo = NULL;
LLAvatarAppearance::LLAvatarXmlInfo* LLAvatarAppearance::sAvatarXmlInfo = NULL;
LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary* LLAvatarAppearance::sAvatarDictionary = NULL;
Nyx (Neal Orman)
committed
LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
mTexSkinColor( NULL ),
mTexHairColor( NULL ),
mTexEyeColor( NULL ),
Nyx (Neal Orman)
committed
mPelvisToFoot(0.f),
mHeadOffset(),
mRoot(NULL),
Brad Payne (Vir Linden)
committed
mWearableData(wearable_data),
Brad Payne (Vir Linden)
committed
mNumBones(0),
mNumCollisionVolumes(0),
mCollisionVolumes(NULL),
mIsBuilt(FALSE),
mInitFlags(0)
llassert_always(mWearableData);
Nyx (Neal Orman)
committed
mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES);
for (U32 i = 0; i < mBakedTextureDatas.size(); i++ )
{
mBakedTextureDatas[i].mLastTextureID = IMG_DEFAULT_AVATAR;
Nyx (Neal Orman)
committed
mBakedTextureDatas[i].mTexLayerSet = NULL;
mBakedTextureDatas[i].mIsLoaded = false;
mBakedTextureDatas[i].mIsUsed = false;
mBakedTextureDatas[i].mMaskTexName = 0;
mBakedTextureDatas[i].mTextureIndex = sAvatarDictionary->bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i);
Nyx (Neal Orman)
committed
}
}
// virtual
void LLAvatarAppearance::initInstance()
{
//-------------------------------------------------------------------------
// initialize joint, mesh and shape members
//-------------------------------------------------------------------------
mRoot = createAvatarJoint();
mRoot->setName( "mRoot" );
for (const auto& mesh_entry_pair : sAvatarDictionary->getMeshEntries())
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);
Richard Linden
committed
switch((S32)mesh_index)
{
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())
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;
for (LLAvatarJointMesh* mesh : mMeshLOD[mesh_index]->mMeshParts)
Richard Linden
committed
mBakedTextureDatas[(S32)baked_texture_index].mJointMeshes.push_back(mesh);
}
}
Nyx (Neal Orman)
committed
buildCharacter();
Nyx (Neal Orman)
committed
mInitFlags |= 1<<0;
Nyx (Neal Orman)
committed
}
// virtual
LLAvatarAppearance::~LLAvatarAppearance()
{
Richard Linden
committed
delete_and_clear(mTexSkinColor);
delete_and_clear(mTexHairColor);
delete_and_clear(mTexEyeColor);
Nyx (Neal Orman)
committed
for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
{
Richard Linden
committed
delete_and_clear(mBakedTextureDatas[i].mTexLayerSet);
mBakedTextureDatas[i].mJointMeshes.clear();
Nyx (Neal Orman)
committed
for (morph_list_t::iterator iter2 = mBakedTextureDatas[i].mMaskedMorphs.begin();
iter2 != mBakedTextureDatas[i].mMaskedMorphs.end(); iter2++)
{
LLMaskedMorph* masked_morph = (*iter2);
delete masked_morph;
}
}
if (mRoot)
{
mRoot->removeAllChildren();
delete mRoot;
}
Nyx (Neal Orman)
committed
mJointMap.clear();
clearSkeleton();
Richard Linden
committed
delete_and_clear_array(mCollisionVolumes);
Nyx (Neal Orman)
committed
std::for_each(mPolyMeshes.begin(), mPolyMeshes.end(), DeletePairedPointer());
mPolyMeshes.clear();
Nyx (Neal Orman)
committed
for (avatar_joint_list_t::iterator jointIter = mMeshLOD.begin();
Nyx (Neal Orman)
committed
jointIter != mMeshLOD.end();
++jointIter)
{
LLAvatarJoint* joint = *jointIter;
Nyx (Neal Orman)
committed
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()
{
initClass("","");
}
Nyx (Neal Orman)
committed
//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 );
Nyx (Neal Orman)
committed
if (!success)
{
LL_ERRS() << "Problem reading avatar configuration file:" << avatar_file_name << LL_ENDL;
Nyx (Neal Orman)
committed
}
// now sanity check xml file
LLXmlTreeNode* root = xml_tree.getRoot();
Nyx (Neal Orman)
committed
if (!root)
{
LL_ERRS() << "No root node found in avatar configuration file: " << avatar_file_name << LL_ENDL;
Nyx (Neal Orman)
committed
return;
}
//-------------------------------------------------------------------------
Brad Payne (Vir Linden)
committed
// <linden_avatar version="2.0"> (root)
Nyx (Neal Orman)
committed
//-------------------------------------------------------------------------
if( !root->hasName( "linden_avatar" ) )
{
LL_ERRS() << "Invalid avatar file header: " << avatar_file_name << LL_ENDL;
Nyx (Neal Orman)
committed
}
std::string version;
static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
Brad Payne (Vir Linden)
committed
if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
Nyx (Neal Orman)
committed
{
LL_ERRS() << "Invalid avatar file version: " << version << " in file: " << avatar_file_name << LL_ENDL;
Nyx (Neal Orman)
committed
}
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;
Nyx (Neal Orman)
committed
return;
}
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;
}
}
Nyx (Neal Orman)
committed
std::string skeleton_path;
LLXmlTree skeleton_xml_tree;
Nyx (Neal Orman)
committed
skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
if (!parseSkeletonFile(skeleton_path, skeleton_xml_tree))
Nyx (Neal Orman)
committed
{
LL_ERRS() << "Error parsing skeleton file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
// 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()))
Nyx (Neal Orman)
committed
{
LL_ERRS() << "Error parsing skeleton XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
// parse avatar_lad.xml
if (sAvatarXmlInfo)
{ //this can happen if a login attempt failed
Richard Linden
committed
delete_and_clear(sAvatarXmlInfo);
Nyx (Neal Orman)
committed
}
sAvatarXmlInfo = new LLAvatarXmlInfo;
if (!sAvatarXmlInfo->parseXmlSkeletonNode(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
if (!sAvatarXmlInfo->parseXmlMeshNodes(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
if (!sAvatarXmlInfo->parseXmlColorNodes(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
if (!sAvatarXmlInfo->parseXmlLayerNodes(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
if (!sAvatarXmlInfo->parseXmlDriverNodes(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
if (!sAvatarXmlInfo->parseXmlMorphNodes(root))
{
LL_ERRS() << "Error parsing skeleton node in avatar XML file: " << skeleton_path << LL_ENDL;
Nyx (Neal Orman)
committed
}
}
void LLAvatarAppearance::cleanupClass()
{
Richard Linden
committed
delete_and_clear(sAvatarXmlInfo);
delete_and_clear(sAvatarDictionary);
delete_and_clear(sAvatarSkeletonInfo);
}
Nyx (Neal Orman)
committed
using namespace LLAvatarAppearanceDefines;
Brad Payne (Vir Linden)
committed
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;
}
}
}
Nyx (Neal Orman)
committed
//------------------------------------------------------------------------
// The viewer can only suggest a good size for the agent,
// the simulator will keep it inside a reasonable range.
void LLAvatarAppearance::computeBodySize()
{
Brad Payne (Vir Linden)
committed
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();
Nyx (Neal Orman)
committed
LLVector3 pelvis_scale = mPelvisp->getScale();
// some of the joints have not been cached
LLVector3 skull = mSkullp->getPosition();
//LLVector3 skull_scale = mSkullp->getScale();
Nyx (Neal Orman)
committed
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);
Nyx (Neal Orman)
committed
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])
Nyx (Neal Orman)
committed
{
mBodySize = new_body_size;
Brad Payne (Vir Linden)
committed
Brad Payne (Vir Linden)
committed
compareJointStateMaps(mLastBodySizeState, mCurrBodySizeState);
Nyx (Neal Orman)
committed
}
}
//-----------------------------------------------------------------------------
// parseSkeletonFile()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree)
Nyx (Neal Orman)
committed
{
//-------------------------------------------------------------------------
// parse the file
//-------------------------------------------------------------------------
BOOL parsesuccess = skeleton_xml_tree.parseFile( filename, FALSE );
Nyx (Neal Orman)
committed
if (!parsesuccess)
{
LL_ERRS() << "Can't parse skeleton file: " << filename << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// now sanity check xml file
LLXmlTreeNode* root = skeleton_xml_tree.getRoot();
Nyx (Neal Orman)
committed
if (!root)
{
LL_ERRS() << "No root node found in avatar skeleton file: " << filename << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
if( !root->hasName( "linden_skeleton" ) )
{
LL_ERRS() << "Invalid avatar skeleton file header: " << filename << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
std::string version;
static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
Brad Payne (Vir Linden)
committed
if( !root->getFastAttributeString( version_string, version ) || ((version != "1.0") && (version != "2.0")))
Nyx (Neal Orman)
committed
{
LL_ERRS() << "Invalid avatar skeleton file version: " << version << " in file: " << filename << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
return TRUE;
}
//-----------------------------------------------------------------------------
// setupBone()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 &volume_num, S32 &joint_num)
{
LLJoint* joint = NULL;
#if SHOW_DEBUG
Brad Payne (Vir Linden)
committed
LL_DEBUGS("BVH") << "bone info: name " << info->mName
<< " isJoint " << info->mIsJoint
<< " volume_num " << volume_num
<< " joint_num " << joint_num
<< LL_ENDL;
#endif
Brad Payne (Vir Linden)
committed
Nyx (Neal Orman)
committed
if (info->mIsJoint)
{
joint = getCharacterJoint(joint_num);
if (!joint)
{
LL_WARNS() << "Too many bones" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
joint->setName( info->mName );
}
else // collision volume
{
if (volume_num >= (S32)mNumCollisionVolumes)
{
Brad Payne (Vir Linden)
committed
LL_WARNS() << "Too many collision volumes" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
joint = (&mCollisionVolumes[volume_num]);
joint->setName( info->mName );
}
// add to parent
Brad Payne (Vir Linden)
committed
if (parent && (joint->getParent()!=parent))
Nyx (Neal Orman)
committed
{
parent->addChild( joint );
}
Brad Payne (Vir Linden)
committed
// SL-315
Nyx (Neal Orman)
committed
joint->setPosition(info->mPos);
joint->setDefaultPosition(info->mPos);
Nyx (Neal Orman)
committed
joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
info->mRot.mV[VZ], LLQuaternion::XYZ));
joint->setScale(info->mScale);
Brad Payne (Vir Linden)
committed
joint->setDefaultScale(info->mScale);
Brad Payne (Vir Linden)
committed
joint->setSupport(info->mSupport);
Brad Payne (Vir Linden)
committed
joint->setEnd(info->mEnd);
Nyx (Neal Orman)
committed
if (info->mIsJoint)
{
joint->setSkinOffset( info->mPivot );
Brad Payne (Vir Linden)
committed
joint->setJointNum(joint_num);
Nyx (Neal Orman)
committed
joint_num++;
}
else // collision volume
{
Brad Payne (Vir Linden)
committed
joint->setJointNum(mNumBones+volume_num);
Nyx (Neal Orman)
committed
volume_num++;
}
Brad Payne (Vir Linden)
committed
Nyx (Neal Orman)
committed
// setup children
Graham Linden
committed
LLAvatarBoneInfo::bones_t::const_iterator iter;
for (iter = info->mChildren.begin(); iter != info->mChildren.end(); ++iter)
Nyx (Neal Orman)
committed
{
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);
Brad Payne (Vir Linden)
committed
mNumBones = num;
return TRUE;
}
Nyx (Neal Orman)
committed
//-----------------------------------------------------------------------------
// buildSkeleton()
//-----------------------------------------------------------------------------
BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
{
Brad Payne (Vir Linden)
committed
LL_DEBUGS("BVH") << "numBones " << info->mNumBones << " numCollisionVolumes " << info->mNumCollisionVolumes << LL_ENDL;
Brad Payne (Vir Linden)
committed
Nyx (Neal Orman)
committed
// allocate joints
if (!allocateCharacterJoints(info->mNumBones))
{
LL_ERRS() << "Can't allocate " << info->mNumBones << " joints" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// allocate volumes
if (info->mNumCollisionVolumes)
{
if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
{
LL_ERRS() << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << LL_ENDL;
Nyx (Neal Orman)
committed
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)
{
Brad Payne (Vir Linden)
committed
LLAvatarBoneInfo *bone_info = *iter;
if (!setupBone(bone_info, NULL, current_volume_num, current_joint_num))
Nyx (Neal Orman)
committed
{
LL_ERRS() << "Error parsing bone in skeleton file" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
}
return TRUE;
}
//-----------------------------------------------------------------------------
// clearSkeleton()
//-----------------------------------------------------------------------------
void LLAvatarAppearance::clearSkeleton()
{
std::for_each(mSkeleton.begin(), mSkeleton.end(), DeletePointer());
mSkeleton.clear();
}
Brad Payne (Vir Linden)
committed
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
//------------------------------------------------------------------------
// 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 );
}
Nyx (Neal Orman)
committed
//-----------------------------------------------------------------------------
// 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);
}
}
Nyx (Neal Orman)
committed
//-------------------------------------------------------------------------
// (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;
Nyx (Neal Orman)
committed
if (!status)
{
if (isSelf())
{
LL_ERRS() << "Unable to load user's avatar" << LL_ENDL;
Nyx (Neal Orman)
committed
}
else
{
LL_WARNS() << "Unable to load other's avatar" << LL_ENDL;
Nyx (Neal Orman)
committed
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
}
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;
Nyx (Neal Orman)
committed
return;
}
//-------------------------------------------------------------------------
// initialize the pelvis
//-------------------------------------------------------------------------
Brad Payne (Vir Linden)
committed
// SL-315
Nyx (Neal Orman)
committed
mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
mIsBuilt = TRUE;
stop_glerror();
}
BOOL LLAvatarAppearance::loadAvatar()
{
// LL_RECORD_BLOCK_TIME(FTM_LOAD_AVATAR);
Nyx (Neal Orman)
committed
// avatar_skeleton.xml
if( !buildSkeleton(sAvatarSkeletonInfo) )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: buildSkeleton() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// avatar_lad.xml : <skeleton>
if( !loadSkeletonNode() )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: loadNodeSkeleton() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// avatar_lad.xml : <mesh>
if( !loadMeshNodes() )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: loadNodeMesh() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// avatar_lad.xml : <global_color>
if( sAvatarXmlInfo->mTexSkinColorInfo )
{
mTexSkinColor = new LLTexGlobalColor( this );
if( !mTexSkinColor->setInfo( sAvatarXmlInfo->mTexSkinColorInfo ) )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: mTexSkinColor->setInfo() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
}
else
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "<global_color> name=\"skin_color\" not found" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
if( sAvatarXmlInfo->mTexHairColorInfo )
{
mTexHairColor = new LLTexGlobalColor( this );
if( !mTexHairColor->setInfo( sAvatarXmlInfo->mTexHairColorInfo ) )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: mTexHairColor->setInfo() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
}
else
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "<global_color> name=\"hair_color\" not found" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
if( sAvatarXmlInfo->mTexEyeColorInfo )
{
mTexEyeColor = new LLTexGlobalColor( this );
if( !mTexEyeColor->setInfo( sAvatarXmlInfo->mTexEyeColorInfo ) )
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: mTexEyeColor->setInfo() failed" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
}
else
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "<global_color> name=\"eye_color\" not found" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// avatar_lad.xml : <layer_set>
if (sAvatarXmlInfo->mLayerInfoList.empty())
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: missing <layer_set> node" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
if (sAvatarXmlInfo->mMorphMaskInfoList.empty())
{
Brad Payne (Vir Linden)
committed
LL_ERRS() << "avatar file: missing <morph_masks> node" << LL_ENDL;
Nyx (Neal Orman)
committed
return FALSE;
}
// avatar_lad.xml : <morph_masks>
for (LLAvatarXmlInfo::morph_info_list_t::iterator iter = sAvatarXmlInfo->mMorphMaskInfoList.begin();
iter != sAvatarXmlInfo->mMorphMaskInfoList.end();
++iter)
{
LLAvatarXmlInfo::LLAvatarMorphInfo *info = *iter;
EBakedTextureIndex baked = sAvatarDictionary->findBakedByRegionName(info->mRegion);
Nyx (Neal Orman)
committed
if (baked != BAKED_NUM_INDICES)
{
LLVisualParam* morph_param;
const std::string *name = &info->mName;
morph_param = getVisualParam(name->c_str());